crewly 1.4.47 → 1.4.49

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 (50) hide show
  1. package/config/skills/agent/core/report-progress/execute.sh +0 -2
  2. package/dist/backend/backend/src/constants.d.ts +48 -0
  3. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  4. package/dist/backend/backend/src/constants.js +48 -0
  5. package/dist/backend/backend/src/constants.js.map +1 -1
  6. package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts.map +1 -1
  7. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js +28 -0
  8. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js.map +1 -1
  9. package/dist/backend/backend/src/controllers/cloud/cloud.routes.d.ts +1 -0
  10. package/dist/backend/backend/src/controllers/cloud/cloud.routes.d.ts.map +1 -1
  11. package/dist/backend/backend/src/controllers/cloud/cloud.routes.js +3 -0
  12. package/dist/backend/backend/src/controllers/cloud/cloud.routes.js.map +1 -1
  13. package/dist/backend/backend/src/controllers/cloud/relay.controller.d.ts +23 -0
  14. package/dist/backend/backend/src/controllers/cloud/relay.controller.d.ts.map +1 -1
  15. package/dist/backend/backend/src/controllers/cloud/relay.controller.js +85 -0
  16. package/dist/backend/backend/src/controllers/cloud/relay.controller.js.map +1 -1
  17. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +11 -0
  18. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  19. package/dist/backend/backend/src/services/agent/agent-registration.service.js +22 -0
  20. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  21. package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts +27 -1
  22. package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
  23. package/dist/backend/backend/src/services/cloud/cloud-client.service.js +54 -0
  24. package/dist/backend/backend/src/services/cloud/cloud-client.service.js.map +1 -1
  25. package/dist/backend/backend/src/services/cloud/cloud-initializer.d.ts.map +1 -1
  26. package/dist/backend/backend/src/services/cloud/cloud-initializer.js +20 -0
  27. package/dist/backend/backend/src/services/cloud/cloud-initializer.js.map +1 -1
  28. package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts +163 -0
  29. package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts.map +1 -0
  30. package/dist/backend/backend/src/services/cloud/cloud-sync.service.js +484 -0
  31. package/dist/backend/backend/src/services/cloud/cloud-sync.service.js.map +1 -0
  32. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +181 -0
  33. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -0
  34. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +91 -0
  35. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -0
  36. package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts.map +1 -1
  37. package/dist/backend/backend/src/services/cloud/relay-client.service.js +2 -0
  38. package/dist/backend/backend/src/services/cloud/relay-client.service.js.map +1 -1
  39. package/dist/backend/backend/src/services/messaging/queue-processor.service.d.ts +86 -0
  40. package/dist/backend/backend/src/services/messaging/queue-processor.service.d.ts.map +1 -1
  41. package/dist/backend/backend/src/services/messaging/queue-processor.service.js +257 -16
  42. package/dist/backend/backend/src/services/messaging/queue-processor.service.js.map +1 -1
  43. package/dist/cli/backend/src/constants.d.ts +48 -0
  44. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  45. package/dist/cli/backend/src/constants.js +48 -0
  46. package/dist/cli/backend/src/constants.js.map +1 -1
  47. package/frontend/dist/assets/{index-7357dbef.js → index-28fcc469.js} +289 -289
  48. package/frontend/dist/assets/{index-a393888e.css → index-78e43622.css} +1 -1
  49. package/frontend/dist/index.html +2 -2
  50. package/package.json +1 -1
@@ -0,0 +1,484 @@
1
+ /**
2
+ * Cloud Sync Service
3
+ *
4
+ * Singleton service that replaces the WebSocket Relay pairing model with a
5
+ * simpler heartbeat + polling architecture. All devices under the same Cloud
6
+ * account are automatically visible and can exchange messages.
7
+ *
8
+ * Responsibilities:
9
+ * - Heartbeat: periodically uploads device status to Cloud
10
+ * - Device poll: periodically fetches the device list for the account
11
+ * - Message poll: periodically fetches pending messages for this device
12
+ * - Send: posts messages to specific devices via Cloud
13
+ *
14
+ * @see docs/cloud-sync-design.md
15
+ * @module services/cloud/cloud-sync.service
16
+ */
17
+ import { EventEmitter } from 'events';
18
+ import { LoggerService } from '../core/logger.service.js';
19
+ import { StorageService } from '../core/storage.service.js';
20
+ import { CLOUD_SYNC_CONSTANTS } from '../../constants.js';
21
+ // ---------------------------------------------------------------------------
22
+ // Helpers
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Read the Crewly version from package.json (best-effort).
26
+ *
27
+ * @returns Version string or 'unknown'
28
+ */
29
+ async function readVersion() {
30
+ try {
31
+ const { readFile } = await import('fs/promises');
32
+ const { join } = await import('path');
33
+ const pkg = JSON.parse(await readFile(join(process.cwd(), 'package.json'), 'utf-8'));
34
+ return pkg.version || 'unknown';
35
+ }
36
+ catch {
37
+ return 'unknown';
38
+ }
39
+ }
40
+ /**
41
+ * Gather active team summaries from StorageService.
42
+ *
43
+ * @returns Array of team summaries for heartbeat payload
44
+ */
45
+ async function gatherTeamSummaries() {
46
+ try {
47
+ const storage = StorageService.getInstance();
48
+ const teams = await storage.getTeams();
49
+ return teams.map((team) => ({
50
+ id: team.id,
51
+ name: team.name,
52
+ memberCount: team.members?.length ?? 0,
53
+ activeAgents: team.members?.filter((m) => m.agentStatus === 'active').length ?? 0,
54
+ }));
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ }
60
+ // ---------------------------------------------------------------------------
61
+ // Service
62
+ // ---------------------------------------------------------------------------
63
+ /**
64
+ * CloudSyncService singleton.
65
+ *
66
+ * Manages heartbeat, device discovery, and message polling for the
67
+ * Cloud Sync system. Extends EventEmitter to notify consumers of
68
+ * device and message changes.
69
+ *
70
+ * Events:
71
+ * - `devices_updated` — Fired when the device list changes. Payload: SyncDevice[]
72
+ * - `message` — Fired for each incoming message. Payload: IncomingMessage
73
+ * - `device_online` — Fired when a device comes online. Payload: SyncDevice
74
+ * - `device_offline` — Fired when a device goes offline. Payload: SyncDevice
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const sync = CloudSyncService.getInstance();
79
+ * sync.start({ cloudUrl, token, deviceId, deviceName });
80
+ * sync.on('message', (msg) => console.log('Received:', msg));
81
+ * sync.on('devices_updated', (devices) => console.log('Devices:', devices));
82
+ * ```
83
+ */
84
+ export class CloudSyncService extends EventEmitter {
85
+ static instance = null;
86
+ logger;
87
+ /** Current service state */
88
+ state = 'stopped';
89
+ /** Configuration (set on start) */
90
+ config = null;
91
+ /** Cached device list from last poll */
92
+ devices = [];
93
+ /** Cached Crewly version */
94
+ version = 'unknown';
95
+ /** Heartbeat timer handle */
96
+ heartbeatTimer = null;
97
+ /** Device poll timer handle */
98
+ devicePollTimer = null;
99
+ /** Message poll timer handle */
100
+ messagePollTimer = null;
101
+ /** Consecutive heartbeat failure count */
102
+ heartbeatFailures = 0;
103
+ /** Consecutive device poll failure count */
104
+ devicePollFailures = 0;
105
+ /** Consecutive message poll failure count */
106
+ messagePollFailures = 0;
107
+ constructor() {
108
+ super();
109
+ this.logger = LoggerService.getInstance().createComponentLogger('CloudSyncService');
110
+ }
111
+ /**
112
+ * Get the singleton instance.
113
+ *
114
+ * @returns CloudSyncService instance
115
+ */
116
+ static getInstance() {
117
+ if (!CloudSyncService.instance) {
118
+ CloudSyncService.instance = new CloudSyncService();
119
+ }
120
+ return CloudSyncService.instance;
121
+ }
122
+ /**
123
+ * Reset the singleton (for testing).
124
+ */
125
+ static resetInstance() {
126
+ if (CloudSyncService.instance) {
127
+ CloudSyncService.instance.stop();
128
+ }
129
+ CloudSyncService.instance = null;
130
+ }
131
+ // -------------------------------------------------------------------------
132
+ // Public API
133
+ // -------------------------------------------------------------------------
134
+ /**
135
+ * Start the Cloud Sync service.
136
+ *
137
+ * Begins heartbeat, device polling, and message polling timers.
138
+ * Performs an immediate heartbeat and device poll on start.
139
+ *
140
+ * @param config - Cloud connection configuration
141
+ */
142
+ start(config) {
143
+ if (this.state === 'syncing') {
144
+ this.logger.warn('CloudSyncService already running, ignoring start()');
145
+ return;
146
+ }
147
+ this.config = config;
148
+ this.state = 'syncing';
149
+ this.heartbeatFailures = 0;
150
+ this.devicePollFailures = 0;
151
+ this.messagePollFailures = 0;
152
+ this.logger.info('Starting Cloud Sync', {
153
+ cloudUrl: config.cloudUrl,
154
+ deviceId: config.deviceId,
155
+ deviceName: config.deviceName,
156
+ });
157
+ // Read version once at startup
158
+ readVersion().then((v) => { this.version = v; }).catch(() => { });
159
+ // Perform initial sync immediately
160
+ this.sendHeartbeat().catch(() => { });
161
+ this.pollDevices().catch(() => { });
162
+ // Start periodic timers
163
+ this.heartbeatTimer = setInterval(() => { this.sendHeartbeat().catch(() => { }); }, CLOUD_SYNC_CONSTANTS.HEARTBEAT_INTERVAL_MS);
164
+ this.devicePollTimer = setInterval(() => { this.pollDevices().catch(() => { }); }, CLOUD_SYNC_CONSTANTS.DEVICE_POLL_INTERVAL_MS);
165
+ this.messagePollTimer = setInterval(() => { this.pollMessages().catch(() => { }); }, CLOUD_SYNC_CONSTANTS.MESSAGE_POLL_INTERVAL_MS);
166
+ // Unref timers so they don't keep the process alive
167
+ if (this.heartbeatTimer.unref)
168
+ this.heartbeatTimer.unref();
169
+ if (this.devicePollTimer.unref)
170
+ this.devicePollTimer.unref();
171
+ if (this.messagePollTimer.unref)
172
+ this.messagePollTimer.unref();
173
+ }
174
+ /**
175
+ * Stop the Cloud Sync service.
176
+ *
177
+ * Clears all timers and resets state. Safe to call multiple times.
178
+ */
179
+ stop() {
180
+ if (this.state === 'stopped')
181
+ return;
182
+ this.logger.info('Stopping Cloud Sync');
183
+ if (this.heartbeatTimer) {
184
+ clearInterval(this.heartbeatTimer);
185
+ this.heartbeatTimer = null;
186
+ }
187
+ if (this.devicePollTimer) {
188
+ clearInterval(this.devicePollTimer);
189
+ this.devicePollTimer = null;
190
+ }
191
+ if (this.messagePollTimer) {
192
+ clearInterval(this.messagePollTimer);
193
+ this.messagePollTimer = null;
194
+ }
195
+ this.state = 'stopped';
196
+ this.config = null;
197
+ this.devices = [];
198
+ this.heartbeatFailures = 0;
199
+ this.devicePollFailures = 0;
200
+ this.messagePollFailures = 0;
201
+ }
202
+ /**
203
+ * Check if the sync service has been started.
204
+ *
205
+ * @returns True if the service is currently syncing
206
+ */
207
+ isStarted() {
208
+ return this.state === 'syncing';
209
+ }
210
+ /**
211
+ * Get the current service state.
212
+ *
213
+ * @returns Current CloudSyncState
214
+ */
215
+ getState() {
216
+ return this.state;
217
+ }
218
+ /**
219
+ * Get all cached devices for this Cloud account.
220
+ *
221
+ * @returns Array of SyncDevice objects (may include offline devices)
222
+ */
223
+ getDevices() {
224
+ return [...this.devices];
225
+ }
226
+ /**
227
+ * Get only online devices (excluding this device).
228
+ *
229
+ * @returns Array of online SyncDevice objects
230
+ */
231
+ getOnlineDevices() {
232
+ return this.devices.filter((d) => d.status === 'online' && !d.isLocal);
233
+ }
234
+ /**
235
+ * Send a message to another device via the Cloud message queue.
236
+ *
237
+ * @param toDeviceId - Target device identifier
238
+ * @param type - Message type
239
+ * @param payload - Message payload (must be JSON-serializable)
240
+ * @throws Error when not started or API call fails
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * await sync.sendMessage('dev-456', 'command', {
245
+ * action: 'delegate-task',
246
+ * content: 'Implement feature X'
247
+ * });
248
+ * ```
249
+ */
250
+ async sendMessage(toDeviceId, type, payload) {
251
+ if (!this.config) {
252
+ throw new Error('CloudSyncService not started. Call start() first.');
253
+ }
254
+ const url = `${this.config.cloudUrl}${CLOUD_SYNC_CONSTANTS.ENDPOINTS.MESSAGES}`;
255
+ const response = await fetch(url, {
256
+ method: 'POST',
257
+ headers: this.authHeaders(),
258
+ body: JSON.stringify({
259
+ to: toDeviceId,
260
+ type,
261
+ payload,
262
+ encrypted: false,
263
+ }),
264
+ signal: AbortSignal.timeout(CLOUD_SYNC_CONSTANTS.REQUEST_TIMEOUT_MS),
265
+ });
266
+ if (!response.ok) {
267
+ const errorText = await response.text().catch(() => '');
268
+ throw new Error(`Failed to send message: ${response.status} ${errorText}`);
269
+ }
270
+ this.logger.info('Message sent via Cloud Sync', { to: toDeviceId, type });
271
+ }
272
+ // -------------------------------------------------------------------------
273
+ // Internal: Heartbeat
274
+ // -------------------------------------------------------------------------
275
+ /**
276
+ * Send a heartbeat to the Cloud server with current device state.
277
+ * Called periodically by the heartbeat timer.
278
+ */
279
+ async sendHeartbeat() {
280
+ if (!this.config)
281
+ return;
282
+ try {
283
+ const teams = await gatherTeamSummaries();
284
+ const payload = {
285
+ deviceId: this.config.deviceId,
286
+ deviceName: this.config.deviceName,
287
+ status: 'online',
288
+ version: this.version,
289
+ capabilities: ['orchestrator', 'agent-runner'],
290
+ teams,
291
+ timestamp: new Date().toISOString(),
292
+ };
293
+ const url = `${this.config.cloudUrl}${CLOUD_SYNC_CONSTANTS.ENDPOINTS.HEARTBEAT}`;
294
+ const response = await fetch(url, {
295
+ method: 'POST',
296
+ headers: this.authHeaders(),
297
+ body: JSON.stringify(payload),
298
+ signal: AbortSignal.timeout(CLOUD_SYNC_CONSTANTS.REQUEST_TIMEOUT_MS),
299
+ });
300
+ if (!response.ok) {
301
+ throw new Error(`Heartbeat failed: ${response.status}`);
302
+ }
303
+ this.heartbeatFailures = 0;
304
+ }
305
+ catch (error) {
306
+ this.heartbeatFailures++;
307
+ this.logger.warn('Heartbeat failed', {
308
+ error: error instanceof Error ? error.message : String(error),
309
+ failures: this.heartbeatFailures,
310
+ });
311
+ this.checkErrorThreshold();
312
+ }
313
+ }
314
+ // -------------------------------------------------------------------------
315
+ // Internal: Device Polling
316
+ // -------------------------------------------------------------------------
317
+ /**
318
+ * Poll the Cloud server for the current device list.
319
+ * Updates the cached device list and emits events on changes.
320
+ */
321
+ async pollDevices() {
322
+ if (!this.config)
323
+ return;
324
+ try {
325
+ const url = `${this.config.cloudUrl}${CLOUD_SYNC_CONSTANTS.ENDPOINTS.DEVICES}`;
326
+ const response = await fetch(url, {
327
+ method: 'GET',
328
+ headers: this.authHeaders(),
329
+ signal: AbortSignal.timeout(CLOUD_SYNC_CONSTANTS.REQUEST_TIMEOUT_MS),
330
+ });
331
+ if (!response.ok) {
332
+ throw new Error(`Device poll failed: ${response.status}`);
333
+ }
334
+ const data = await response.json();
335
+ if (!data.success || !data.devices) {
336
+ throw new Error('Device poll returned unsuccessful response');
337
+ }
338
+ const previousOnlineIds = new Set(this.devices.filter((d) => d.status === 'online').map((d) => d.deviceId));
339
+ // Normalize devices: mark local, determine online status
340
+ const offlineThreshold = CLOUD_SYNC_CONSTANTS.OFFLINE_THRESHOLD_MS;
341
+ const now = Date.now();
342
+ const updatedDevices = data.devices.map((d) => ({
343
+ ...d,
344
+ status: (now - new Date(d.lastHeartbeatAt).getTime() > offlineThreshold)
345
+ ? 'offline'
346
+ : 'online',
347
+ isLocal: d.deviceId === this.config.deviceId,
348
+ }));
349
+ // Detect online/offline transitions
350
+ const newOnlineIds = new Set(updatedDevices.filter((d) => d.status === 'online').map((d) => d.deviceId));
351
+ for (const device of updatedDevices) {
352
+ if (device.status === 'online' && !previousOnlineIds.has(device.deviceId) && !device.isLocal) {
353
+ this.emit('device_online', device);
354
+ }
355
+ }
356
+ for (const prevId of previousOnlineIds) {
357
+ if (!newOnlineIds.has(prevId) && prevId !== this.config.deviceId) {
358
+ const offlineDevice = this.devices.find((d) => d.deviceId === prevId);
359
+ if (offlineDevice) {
360
+ this.emit('device_offline', { ...offlineDevice, status: 'offline' });
361
+ }
362
+ }
363
+ }
364
+ this.devices = updatedDevices;
365
+ this.devicePollFailures = 0;
366
+ this.emit('devices_updated', this.getDevices());
367
+ }
368
+ catch (error) {
369
+ this.devicePollFailures++;
370
+ this.logger.warn('Device poll failed', {
371
+ error: error instanceof Error ? error.message : String(error),
372
+ failures: this.devicePollFailures,
373
+ });
374
+ this.checkErrorThreshold();
375
+ }
376
+ }
377
+ // -------------------------------------------------------------------------
378
+ // Internal: Message Polling
379
+ // -------------------------------------------------------------------------
380
+ /**
381
+ * Poll the Cloud server for pending messages addressed to this device.
382
+ * Emits 'message' event for each received message, then acknowledges.
383
+ */
384
+ async pollMessages() {
385
+ if (!this.config)
386
+ return;
387
+ try {
388
+ const url = `${this.config.cloudUrl}${CLOUD_SYNC_CONSTANTS.ENDPOINTS.MESSAGES}?deviceId=${encodeURIComponent(this.config.deviceId)}&limit=50`;
389
+ const response = await fetch(url, {
390
+ method: 'GET',
391
+ headers: this.authHeaders(),
392
+ signal: AbortSignal.timeout(CLOUD_SYNC_CONSTANTS.REQUEST_TIMEOUT_MS),
393
+ });
394
+ if (!response.ok) {
395
+ throw new Error(`Message poll failed: ${response.status}`);
396
+ }
397
+ const data = await response.json();
398
+ if (!data.success || !data.messages || data.messages.length === 0) {
399
+ this.messagePollFailures = 0;
400
+ return;
401
+ }
402
+ // Emit each message
403
+ const messageIds = [];
404
+ for (const msg of data.messages) {
405
+ this.emit('message', msg);
406
+ messageIds.push(msg.id);
407
+ }
408
+ // Acknowledge processed messages
409
+ await this.ackMessages(messageIds);
410
+ this.messagePollFailures = 0;
411
+ this.logger.debug('Polled and processed messages', { count: data.messages.length });
412
+ }
413
+ catch (error) {
414
+ this.messagePollFailures++;
415
+ this.logger.warn('Message poll failed', {
416
+ error: error instanceof Error ? error.message : String(error),
417
+ failures: this.messagePollFailures,
418
+ });
419
+ this.checkErrorThreshold();
420
+ }
421
+ }
422
+ /**
423
+ * Acknowledge processed messages so Cloud can remove them from the queue.
424
+ *
425
+ * @param messageIds - Array of message IDs to acknowledge
426
+ */
427
+ async ackMessages(messageIds) {
428
+ if (!this.config || messageIds.length === 0)
429
+ return;
430
+ try {
431
+ const url = `${this.config.cloudUrl}${CLOUD_SYNC_CONSTANTS.ENDPOINTS.MESSAGES_ACK}`;
432
+ await fetch(url, {
433
+ method: 'POST',
434
+ headers: this.authHeaders(),
435
+ body: JSON.stringify({
436
+ deviceId: this.config.deviceId,
437
+ messageIds,
438
+ }),
439
+ signal: AbortSignal.timeout(CLOUD_SYNC_CONSTANTS.REQUEST_TIMEOUT_MS),
440
+ });
441
+ }
442
+ catch (error) {
443
+ this.logger.warn('Message ACK failed (non-fatal)', {
444
+ error: error instanceof Error ? error.message : String(error),
445
+ messageIds,
446
+ });
447
+ }
448
+ }
449
+ // -------------------------------------------------------------------------
450
+ // Internal: Error Handling
451
+ // -------------------------------------------------------------------------
452
+ /**
453
+ * Check if any failure counter has exceeded the threshold.
454
+ * If so, transition to error state.
455
+ */
456
+ checkErrorThreshold() {
457
+ const max = CLOUD_SYNC_CONSTANTS.MAX_CONSECUTIVE_FAILURES;
458
+ if (this.heartbeatFailures >= max ||
459
+ this.devicePollFailures >= max ||
460
+ this.messagePollFailures >= max) {
461
+ this.logger.error('Cloud Sync entering error state after repeated failures', {
462
+ heartbeatFailures: this.heartbeatFailures,
463
+ devicePollFailures: this.devicePollFailures,
464
+ messagePollFailures: this.messagePollFailures,
465
+ });
466
+ this.state = 'error';
467
+ }
468
+ }
469
+ // -------------------------------------------------------------------------
470
+ // Internal: Helpers
471
+ // -------------------------------------------------------------------------
472
+ /**
473
+ * Build authorization headers for Cloud API requests.
474
+ *
475
+ * @returns Headers with Bearer token and Content-Type
476
+ */
477
+ authHeaders() {
478
+ return {
479
+ Authorization: `Bearer ${this.config?.token ?? ''}`,
480
+ 'Content-Type': 'application/json',
481
+ };
482
+ }
483
+ }
484
+ //# sourceMappingURL=cloud-sync.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-sync.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/cloud-sync.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Cloud Sync Type Definitions
3
+ *
4
+ * Defines the data structures for the Cloud Sync system that replaces
5
+ * the WebSocket Relay pairing model. All devices under the same Cloud
6
+ * account are automatically visible and can exchange messages.
7
+ *
8
+ * @see docs/cloud-sync-design.md
9
+ * @module services/cloud/cloud-sync.types
10
+ */
11
+ /** Possible states of the CloudSyncService lifecycle. */
12
+ export type CloudSyncState = 'stopped' | 'syncing' | 'error';
13
+ /** Valid cloud sync state values for runtime validation. */
14
+ export declare const CLOUD_SYNC_STATES: readonly ["stopped", "syncing", "error"];
15
+ /**
16
+ * Configuration required to start the CloudSyncService.
17
+ * Obtained from CloudClientService credentials and DeviceIdentityService.
18
+ */
19
+ export interface CloudSyncConfig {
20
+ /** Cloud API base URL (e.g. "https://api.crewlyai.com") */
21
+ cloudUrl: string;
22
+ /** Bearer token for Cloud API authentication */
23
+ token: string;
24
+ /** Unique device identifier from ~/.crewly/device.json */
25
+ deviceId: string;
26
+ /** Human-readable device name (OS hostname) */
27
+ deviceName: string;
28
+ }
29
+ /** Summary of a team on a device, included in heartbeat payloads. */
30
+ export interface SyncTeamSummary {
31
+ /** Team identifier */
32
+ id: string;
33
+ /** Team display name */
34
+ name: string;
35
+ /** Number of configured team members */
36
+ memberCount: number;
37
+ /** Number of currently active agents */
38
+ activeAgents: number;
39
+ }
40
+ /**
41
+ * A device visible in the same Cloud account.
42
+ * Populated by polling GET /v1/devices.
43
+ */
44
+ export interface SyncDevice {
45
+ /** Unique device identifier (UUID) */
46
+ deviceId: string;
47
+ /** Human-readable device name (hostname) */
48
+ deviceName: string;
49
+ /** Whether the device is online or offline (based on heartbeat freshness) */
50
+ status: 'online' | 'offline';
51
+ /** Crewly version running on the device */
52
+ version?: string;
53
+ /** Device capabilities (e.g. orchestrator, agent-runner, mcp-server) */
54
+ capabilities?: string[];
55
+ /** Active teams on the device */
56
+ teams?: SyncTeamSummary[];
57
+ /** ISO timestamp of last heartbeat */
58
+ lastHeartbeatAt: string;
59
+ /** Whether this device is the local OSS instance */
60
+ isLocal?: boolean;
61
+ }
62
+ /**
63
+ * Payload sent to Cloud in the heartbeat POST.
64
+ * Uploaded every HEARTBEAT_INTERVAL_MS (30s).
65
+ */
66
+ export interface HeartbeatPayload {
67
+ /** Unique device identifier */
68
+ deviceId: string;
69
+ /** Human-readable device name (hostname) */
70
+ deviceName: string;
71
+ /** Device status */
72
+ status: 'online';
73
+ /** Crewly version */
74
+ version: string;
75
+ /** Device capabilities */
76
+ capabilities: string[];
77
+ /** Active teams on this device */
78
+ teams: SyncTeamSummary[];
79
+ /** ISO timestamp */
80
+ timestamp: string;
81
+ }
82
+ /** All supported message types for inter-device communication. */
83
+ export type MessageType = 'command' | 'sync_teams' | 'sync_settings' | 'notification' | 'relay' | 'ping' | 'task_update';
84
+ /** Valid message type values for runtime validation. */
85
+ export declare const MESSAGE_TYPES: readonly MessageType[];
86
+ /**
87
+ * An incoming message received from another device via Cloud polling.
88
+ */
89
+ export interface IncomingMessage {
90
+ /** Unique message identifier */
91
+ id: string;
92
+ /** Device ID of the sender */
93
+ from: string;
94
+ /** Human-readable name of the sending device */
95
+ fromDeviceName: string;
96
+ /** Message type */
97
+ type: MessageType;
98
+ /** Message payload (structure varies by type) */
99
+ payload: unknown;
100
+ /** Whether the payload is E2EE encrypted */
101
+ encrypted: boolean;
102
+ /** ISO timestamp when the message was sent */
103
+ sentAt: string;
104
+ }
105
+ /**
106
+ * Payload for a command message sent between devices.
107
+ */
108
+ export interface CommandPayload {
109
+ /** The action to perform */
110
+ action: string;
111
+ /** Optional task identifier */
112
+ taskId?: string;
113
+ /** Optional team identifier */
114
+ teamId?: string;
115
+ /** Optional agent session name */
116
+ agentSession?: string;
117
+ /** Free-text content */
118
+ content?: string;
119
+ }
120
+ /**
121
+ * Payload for a notification message.
122
+ */
123
+ export interface NotificationPayload {
124
+ /** Notification title */
125
+ title: string;
126
+ /** Notification body */
127
+ message: string;
128
+ /** Urgency level */
129
+ urgency: 'low' | 'normal' | 'high';
130
+ /** Optional team identifier */
131
+ teamId?: string;
132
+ }
133
+ /**
134
+ * Payload for a task_update message.
135
+ */
136
+ export interface TaskUpdatePayload {
137
+ /** Task identifier */
138
+ taskId: string;
139
+ /** Current task status */
140
+ status: string;
141
+ /** Progress percentage (0-100) */
142
+ progress: number;
143
+ /** Summary of what was done */
144
+ summary?: string;
145
+ }
146
+ /**
147
+ * Check if a value is a valid CloudSyncState.
148
+ *
149
+ * @param value - Value to check
150
+ * @returns True if the value is a valid CloudSyncState
151
+ */
152
+ export declare function isCloudSyncState(value: unknown): value is CloudSyncState;
153
+ /**
154
+ * Check if a value is a valid MessageType.
155
+ *
156
+ * @param value - Value to check
157
+ * @returns True if the value is a valid MessageType
158
+ */
159
+ export declare function isMessageType(value: unknown): value is MessageType;
160
+ /**
161
+ * Check if an object satisfies the SyncDevice interface shape.
162
+ *
163
+ * @param value - Value to check
164
+ * @returns True if the value has the required SyncDevice fields
165
+ */
166
+ export declare function isSyncDevice(value: unknown): value is SyncDevice;
167
+ /**
168
+ * Check if an object satisfies the IncomingMessage interface shape.
169
+ *
170
+ * @param value - Value to check
171
+ * @returns True if the value has the required IncomingMessage fields
172
+ */
173
+ export declare function isIncomingMessage(value: unknown): value is IncomingMessage;
174
+ /**
175
+ * Check if an object satisfies the CloudSyncConfig interface shape.
176
+ *
177
+ * @param value - Value to check
178
+ * @returns True if the value has the required CloudSyncConfig fields
179
+ */
180
+ export declare function isCloudSyncConfig(value: unknown): value is CloudSyncConfig;
181
+ //# sourceMappingURL=cloud-sync.types.d.ts.map