crewly 1.8.7 → 1.8.9

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 (165) hide show
  1. package/dist/backend/backend/src/constants.d.ts +12 -0
  2. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  3. package/dist/backend/backend/src/constants.js +12 -0
  4. package/dist/backend/backend/src/constants.js.map +1 -1
  5. package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts.map +1 -1
  6. package/dist/backend/backend/src/controllers/browser/browser.controller.js +17 -0
  7. package/dist/backend/backend/src/controllers/browser/browser.controller.js.map +1 -1
  8. package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts.map +1 -1
  9. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js +8 -1
  10. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js.map +1 -1
  11. package/dist/backend/backend/src/index.d.ts.map +1 -1
  12. package/dist/backend/backend/src/index.js +15 -7
  13. package/dist/backend/backend/src/index.js.map +1 -1
  14. package/dist/backend/backend/src/services/browser/browser-bridge.service.d.ts +7 -0
  15. package/dist/backend/backend/src/services/browser/browser-bridge.service.d.ts.map +1 -1
  16. package/dist/backend/backend/src/services/browser/browser-bridge.service.js +69 -12
  17. package/dist/backend/backend/src/services/browser/browser-bridge.service.js.map +1 -1
  18. package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts +122 -1
  19. package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts.map +1 -1
  20. package/dist/backend/backend/src/services/browser/browser-proxy.service.js +252 -17
  21. package/dist/backend/backend/src/services/browser/browser-proxy.service.js.map +1 -1
  22. package/dist/backend/backend/src/services/browser/browser-relay-adapter.service.d.ts +37 -3
  23. package/dist/backend/backend/src/services/browser/browser-relay-adapter.service.d.ts.map +1 -1
  24. package/dist/backend/backend/src/services/browser/browser-relay-adapter.service.js +140 -23
  25. package/dist/backend/backend/src/services/browser/browser-relay-adapter.service.js.map +1 -1
  26. package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts +75 -0
  27. package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
  28. package/dist/backend/backend/src/services/cloud/cloud-client.service.js +164 -12
  29. package/dist/backend/backend/src/services/cloud/cloud-client.service.js.map +1 -1
  30. package/dist/cli/backend/src/constants.d.ts +12 -0
  31. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  32. package/dist/cli/backend/src/constants.js +12 -0
  33. package/dist/cli/backend/src/constants.js.map +1 -1
  34. package/dist/cli/cli/src/index.js +0 -0
  35. package/package.json +1 -1
  36. package/config/constants.d.ts.map +0 -1
  37. package/config/index.d.ts.map +0 -1
  38. package/dist/backend/backend/src/controllers/task-management/task-management.controller.d.ts +0 -169
  39. package/dist/backend/backend/src/controllers/task-management/task-management.controller.d.ts.map +0 -1
  40. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js +0 -1779
  41. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js.map +0 -1
  42. package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.d.ts +0 -513
  43. package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.d.ts.map +0 -1
  44. package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.js +0 -1568
  45. package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.js.map +0 -1
  46. package/dist/backend/backend/src/services/agent/crewly-agent/agent-worker.d.ts +0 -86
  47. package/dist/backend/backend/src/services/agent/crewly-agent/agent-worker.d.ts.map +0 -1
  48. package/dist/backend/backend/src/services/agent/crewly-agent/agent-worker.js +0 -147
  49. package/dist/backend/backend/src/services/agent/crewly-agent/agent-worker.js.map +0 -1
  50. package/dist/backend/backend/src/services/agent/crewly-agent/api-client.d.ts +0 -68
  51. package/dist/backend/backend/src/services/agent/crewly-agent/api-client.d.ts.map +0 -1
  52. package/dist/backend/backend/src/services/agent/crewly-agent/api-client.js +0 -131
  53. package/dist/backend/backend/src/services/agent/crewly-agent/api-client.js.map +0 -1
  54. package/dist/backend/backend/src/services/agent/crewly-agent/audit-log.service.d.ts +0 -130
  55. package/dist/backend/backend/src/services/agent/crewly-agent/audit-log.service.d.ts.map +0 -1
  56. package/dist/backend/backend/src/services/agent/crewly-agent/audit-log.service.js +0 -263
  57. package/dist/backend/backend/src/services/agent/crewly-agent/audit-log.service.js.map +0 -1
  58. package/dist/backend/backend/src/services/agent/crewly-agent/audit-trail.service.d.ts +0 -74
  59. package/dist/backend/backend/src/services/agent/crewly-agent/audit-trail.service.d.ts.map +0 -1
  60. package/dist/backend/backend/src/services/agent/crewly-agent/audit-trail.service.js +0 -140
  61. package/dist/backend/backend/src/services/agent/crewly-agent/audit-trail.service.js.map +0 -1
  62. package/dist/backend/backend/src/services/agent/crewly-agent/auditor-tools.d.ts +0 -29
  63. package/dist/backend/backend/src/services/agent/crewly-agent/auditor-tools.d.ts.map +0 -1
  64. package/dist/backend/backend/src/services/agent/crewly-agent/auditor-tools.js +0 -279
  65. package/dist/backend/backend/src/services/agent/crewly-agent/auditor-tools.js.map +0 -1
  66. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-runtime.service.d.ts +0 -340
  67. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-runtime.service.d.ts.map +0 -1
  68. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-runtime.service.js +0 -1176
  69. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-runtime.service.js.map +0 -1
  70. package/dist/backend/backend/src/services/agent/crewly-agent/deepseek-sse-transform.d.ts +0 -79
  71. package/dist/backend/backend/src/services/agent/crewly-agent/deepseek-sse-transform.d.ts.map +0 -1
  72. package/dist/backend/backend/src/services/agent/crewly-agent/deepseek-sse-transform.js +0 -145
  73. package/dist/backend/backend/src/services/agent/crewly-agent/deepseek-sse-transform.js.map +0 -1
  74. package/dist/backend/backend/src/services/agent/crewly-agent/env-isolation.service.d.ts +0 -79
  75. package/dist/backend/backend/src/services/agent/crewly-agent/env-isolation.service.d.ts.map +0 -1
  76. package/dist/backend/backend/src/services/agent/crewly-agent/env-isolation.service.js +0 -218
  77. package/dist/backend/backend/src/services/agent/crewly-agent/env-isolation.service.js.map +0 -1
  78. package/dist/backend/backend/src/services/agent/crewly-agent/index.d.ts +0 -16
  79. package/dist/backend/backend/src/services/agent/crewly-agent/index.d.ts.map +0 -1
  80. package/dist/backend/backend/src/services/agent/crewly-agent/index.js +0 -16
  81. package/dist/backend/backend/src/services/agent/crewly-agent/index.js.map +0 -1
  82. package/dist/backend/backend/src/services/agent/crewly-agent/mcp-tool-bridge.d.ts +0 -135
  83. package/dist/backend/backend/src/services/agent/crewly-agent/mcp-tool-bridge.d.ts.map +0 -1
  84. package/dist/backend/backend/src/services/agent/crewly-agent/mcp-tool-bridge.js +0 -185
  85. package/dist/backend/backend/src/services/agent/crewly-agent/mcp-tool-bridge.js.map +0 -1
  86. package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.d.ts +0 -141
  87. package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.d.ts.map +0 -1
  88. package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.js +0 -310
  89. package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.js.map +0 -1
  90. package/dist/backend/backend/src/services/agent/crewly-agent/output-filter.service.d.ts +0 -91
  91. package/dist/backend/backend/src/services/agent/crewly-agent/output-filter.service.d.ts.map +0 -1
  92. package/dist/backend/backend/src/services/agent/crewly-agent/output-filter.service.js +0 -143
  93. package/dist/backend/backend/src/services/agent/crewly-agent/output-filter.service.js.map +0 -1
  94. package/dist/backend/backend/src/services/agent/crewly-agent/prompt-guard.service.d.ts +0 -103
  95. package/dist/backend/backend/src/services/agent/crewly-agent/prompt-guard.service.d.ts.map +0 -1
  96. package/dist/backend/backend/src/services/agent/crewly-agent/prompt-guard.service.js +0 -256
  97. package/dist/backend/backend/src/services/agent/crewly-agent/prompt-guard.service.js.map +0 -1
  98. package/dist/backend/backend/src/services/agent/crewly-agent/rate-limiter.d.ts +0 -143
  99. package/dist/backend/backend/src/services/agent/crewly-agent/rate-limiter.d.ts.map +0 -1
  100. package/dist/backend/backend/src/services/agent/crewly-agent/rate-limiter.js +0 -264
  101. package/dist/backend/backend/src/services/agent/crewly-agent/rate-limiter.js.map +0 -1
  102. package/dist/backend/backend/src/services/agent/crewly-agent/smoke-test.d.ts +0 -13
  103. package/dist/backend/backend/src/services/agent/crewly-agent/smoke-test.d.ts.map +0 -1
  104. package/dist/backend/backend/src/services/agent/crewly-agent/smoke-test.js +0 -91
  105. package/dist/backend/backend/src/services/agent/crewly-agent/smoke-test.js.map +0 -1
  106. package/dist/backend/backend/src/services/agent/crewly-agent/tool-registry.d.ts +0 -135
  107. package/dist/backend/backend/src/services/agent/crewly-agent/tool-registry.d.ts.map +0 -1
  108. package/dist/backend/backend/src/services/agent/crewly-agent/tool-registry.js +0 -1937
  109. package/dist/backend/backend/src/services/agent/crewly-agent/tool-registry.js.map +0 -1
  110. package/dist/backend/backend/src/services/autonomous/auto-assign.service.d.ts +0 -429
  111. package/dist/backend/backend/src/services/autonomous/auto-assign.service.d.ts.map +0 -1
  112. package/dist/backend/backend/src/services/autonomous/auto-assign.service.js +0 -852
  113. package/dist/backend/backend/src/services/autonomous/auto-assign.service.js.map +0 -1
  114. package/dist/backend/backend/src/services/project/task-tracking.service.d.ts +0 -171
  115. package/dist/backend/backend/src/services/project/task-tracking.service.d.ts.map +0 -1
  116. package/dist/backend/backend/src/services/project/task-tracking.service.js +0 -725
  117. package/dist/backend/backend/src/services/project/task-tracking.service.js.map +0 -1
  118. package/dist/backend/backend/src/services/v3/project-task-watcher.service.d.ts +0 -118
  119. package/dist/backend/backend/src/services/v3/project-task-watcher.service.d.ts.map +0 -1
  120. package/dist/backend/backend/src/services/v3/project-task-watcher.service.js +0 -326
  121. package/dist/backend/backend/src/services/v3/project-task-watcher.service.js.map +0 -1
  122. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts +0 -74
  123. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts.map +0 -1
  124. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js +0 -154
  125. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js.map +0 -1
  126. package/dist/backend/backend/src/types/auto-assign.types.d.ts +0 -271
  127. package/dist/backend/backend/src/types/auto-assign.types.d.ts.map +0 -1
  128. package/dist/backend/backend/src/types/auto-assign.types.js +0 -136
  129. package/dist/backend/backend/src/types/auto-assign.types.js.map +0 -1
  130. package/dist/backend/backend/src/utils/esm-require.utils.d.ts +0 -111
  131. package/dist/backend/backend/src/utils/esm-require.utils.d.ts.map +0 -1
  132. package/dist/backend/backend/src/utils/esm-require.utils.js +0 -124
  133. package/dist/backend/backend/src/utils/esm-require.utils.js.map +0 -1
  134. package/dist/cli/backend/src/services/ai/prompt-modules/prompt-module.interface.d.ts +0 -220
  135. package/dist/cli/backend/src/services/ai/prompt-modules/prompt-module.interface.d.ts.map +0 -1
  136. package/dist/cli/backend/src/services/ai/prompt-modules/prompt-module.interface.js +0 -37
  137. package/dist/cli/backend/src/services/ai/prompt-modules/prompt-module.interface.js.map +0 -1
  138. package/dist/cli/backend/src/services/knowledge/fts5-search-strategy.d.ts +0 -56
  139. package/dist/cli/backend/src/services/knowledge/fts5-search-strategy.d.ts.map +0 -1
  140. package/dist/cli/backend/src/services/knowledge/fts5-search-strategy.js +0 -91
  141. package/dist/cli/backend/src/services/knowledge/fts5-search-strategy.js.map +0 -1
  142. package/dist/cli/backend/src/services/knowledge/learnings-index.service.d.ts +0 -159
  143. package/dist/cli/backend/src/services/knowledge/learnings-index.service.d.ts.map +0 -1
  144. package/dist/cli/backend/src/services/knowledge/learnings-index.service.js +0 -304
  145. package/dist/cli/backend/src/services/knowledge/learnings-index.service.js.map +0 -1
  146. package/dist/cli/backend/src/services/knowledge/wiki-compiler.service.d.ts +0 -115
  147. package/dist/cli/backend/src/services/knowledge/wiki-compiler.service.d.ts.map +0 -1
  148. package/dist/cli/backend/src/services/knowledge/wiki-compiler.service.js +0 -215
  149. package/dist/cli/backend/src/services/knowledge/wiki-compiler.service.js.map +0 -1
  150. package/dist/cli/backend/src/services/memory/embedding-provider.d.ts +0 -78
  151. package/dist/cli/backend/src/services/memory/embedding-provider.d.ts.map +0 -1
  152. package/dist/cli/backend/src/services/memory/embedding-provider.js +0 -179
  153. package/dist/cli/backend/src/services/memory/embedding-provider.js.map +0 -1
  154. package/dist/cli/backend/src/services/memory/vector-store.service.d.ts +0 -331
  155. package/dist/cli/backend/src/services/memory/vector-store.service.d.ts.map +0 -1
  156. package/dist/cli/backend/src/services/memory/vector-store.service.js +0 -814
  157. package/dist/cli/backend/src/services/memory/vector-store.service.js.map +0 -1
  158. package/dist/cli/backend/src/services/project/task-tracking.service.d.ts +0 -171
  159. package/dist/cli/backend/src/services/project/task-tracking.service.d.ts.map +0 -1
  160. package/dist/cli/backend/src/services/project/task-tracking.service.js +0 -725
  161. package/dist/cli/backend/src/services/project/task-tracking.service.js.map +0 -1
  162. package/dist/cli/backend/src/types/auto-assign.types.d.ts +0 -271
  163. package/dist/cli/backend/src/types/auto-assign.types.d.ts.map +0 -1
  164. package/dist/cli/backend/src/types/auto-assign.types.js +0 -136
  165. package/dist/cli/backend/src/types/auto-assign.types.js.map +0 -1
@@ -54,6 +54,18 @@ export class BrowserRelayAdapter {
54
54
  boundDeviceOnlineHandler = null;
55
55
  boundDeviceOfflineHandler = null;
56
56
  boundDevicesUpdatedHandler = null;
57
+ /**
58
+ * Bound handlers for the BrowserProxy event channel — the primary
59
+ * signal source after 2026-05-23. The Sync handlers above remain wired
60
+ * as a belt-and-suspenders fallback for legacy code paths that still
61
+ * push browser devices through Sync, but in current production those
62
+ * events never fire (CloudSyncService only tracks orchestrator-role
63
+ * devices). The proxy listens directly to the Cloud relay's browser_list
64
+ * / browser_event channel, which is the canonical source of truth for
65
+ * extension presence.
66
+ */
67
+ boundProxyConnectedHandler = null;
68
+ boundProxyDisconnectedHandler = null;
57
69
  constructor() {
58
70
  this.logger = LoggerService.getInstance().createComponentLogger('BrowserRelayAdapter');
59
71
  }
@@ -96,17 +108,29 @@ export class BrowserRelayAdapter {
96
108
  return this.extensionDeviceId;
97
109
  }
98
110
  /**
99
- * Whether the relay adapter is ready to send commands.
100
- * Requires an Extension device ID and an active CloudSyncService.
111
+ * Whether the relay adapter has a known extension target via the
112
+ * BrowserProxy event channel.
113
+ *
114
+ * Single liveness definition (2026-05-28 browser-relay fix): availability is
115
+ * derived from the BrowserProxy — the canonical source of truth for browser
116
+ * presence on the Cloud relay — NOT from the CloudSyncService state. The
117
+ * former CloudSync `getState() === 'syncing'` gate was dead in production
118
+ * (Sync only tracks orchestrator-role devices, never browser-role), so it
119
+ * reported a liveness that disagreed with `proxy.isAvailable()`. Collapsing
120
+ * to the proxy removes that disagreement (REGISTRY-TRUTH invariant).
101
121
  *
102
- * @returns True if relay is available
122
+ * @returns True if the proxy reports a drivable browser and we have a target
103
123
  */
104
124
  isAvailable() {
105
125
  if (!this.extensionDeviceId)
106
126
  return false;
107
127
  try {
108
- const sync = CloudSyncService.getInstance();
109
- return sync.getState() === 'syncing';
128
+ // Lazy require to avoid an import-time circular dep — proxy and adapter
129
+ // both live under backend/src/services/browser/.
130
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
131
+ const mod = require('./browser-proxy.service.js');
132
+ const proxy = mod?.BrowserProxyService?.getInstance?.();
133
+ return proxy ? proxy.isAvailable() : false;
110
134
  }
111
135
  catch {
112
136
  return false;
@@ -196,28 +220,69 @@ export class BrowserRelayAdapter {
196
220
  if (this.discoveryActive)
197
221
  return;
198
222
  try {
223
+ // ---- Primary signal source: BrowserProxy events (2026-05-23) ----
224
+ // The proxy listens directly to the Cloud relay's browser channel
225
+ // (browser_list / browser_event). It's the only path that reliably
226
+ // sees the extension; the Sync-driven path below is kept for
227
+ // backward compat but typically observes nothing.
228
+ // Dynamic import to avoid circular-dep at module load (proxy and
229
+ // adapter both live under backend/src/services/browser/).
230
+ void (async () => {
231
+ try {
232
+ const { BrowserProxyService } = await import('./browser-proxy.service.js');
233
+ const proxy = BrowserProxyService.getInstance();
234
+ this.boundProxyConnectedHandler = (evt) => {
235
+ this.onProxyInstanceConnected(evt);
236
+ };
237
+ this.boundProxyDisconnectedHandler = (evt) => {
238
+ this.onProxyInstanceDisconnected(evt);
239
+ };
240
+ proxy.events.on('instance_connected', this.boundProxyConnectedHandler);
241
+ proxy.events.on('instance_disconnected', this.boundProxyDisconnectedHandler);
242
+ // Catch-up scan: if the proxy already has instances at the time
243
+ // we subscribe (e.g. boot ordering or adapter restart), seed our
244
+ // target now so we don't wait for the next browser_event.
245
+ const seed = proxy.getInstances();
246
+ if (seed.length > 0 && this.extensionDeviceId === null) {
247
+ const first = seed[0];
248
+ this.extensionDeviceId = first.instanceId;
249
+ this.logger.info('Browser extension seeded from existing proxy instances', {
250
+ instanceId: first.instanceId,
251
+ instanceName: first.instanceName,
252
+ });
253
+ }
254
+ }
255
+ catch (err) {
256
+ this.logger.warn('Failed to subscribe to BrowserProxy events', {
257
+ error: err instanceof Error ? err.message : String(err),
258
+ });
259
+ }
260
+ })();
261
+ // ---- Fallback signal source: CloudSyncService device events ----
262
+ // Kept for compat with any deployment that still routes browser
263
+ // device announcements through Sync; harmless when Sync omits them.
199
264
  const sync = CloudSyncService.getInstance();
200
265
  if (!sync.isStarted()) {
201
- this.logger.debug('CloudSync not started — deferring auto-discovery');
202
- return;
266
+ this.logger.debug('CloudSync not started — proxy events only');
267
+ }
268
+ else {
269
+ this.boundDeviceOnlineHandler = (device) => {
270
+ this.onDeviceOnline(device);
271
+ };
272
+ this.boundDeviceOfflineHandler = (device) => {
273
+ this.onDeviceOffline(device);
274
+ };
275
+ this.boundDevicesUpdatedHandler = (devices) => {
276
+ this.onDevicesUpdated(devices);
277
+ };
278
+ sync.on('device_online', this.boundDeviceOnlineHandler);
279
+ sync.on('device_offline', this.boundDeviceOfflineHandler);
280
+ sync.on('devices_updated', this.boundDevicesUpdatedHandler);
281
+ // Scan existing device list for already-online browser extensions
282
+ const existingDevices = sync.getDevices();
283
+ this.scanForBrowserDevice(existingDevices);
203
284
  }
204
- // Bind event handlers
205
- this.boundDeviceOnlineHandler = (device) => {
206
- this.onDeviceOnline(device);
207
- };
208
- this.boundDeviceOfflineHandler = (device) => {
209
- this.onDeviceOffline(device);
210
- };
211
- this.boundDevicesUpdatedHandler = (devices) => {
212
- this.onDevicesUpdated(devices);
213
- };
214
- sync.on('device_online', this.boundDeviceOnlineHandler);
215
- sync.on('device_offline', this.boundDeviceOfflineHandler);
216
- sync.on('devices_updated', this.boundDevicesUpdatedHandler);
217
285
  this.discoveryActive = true;
218
- // Scan existing device list for already-online browser extensions
219
- const existingDevices = sync.getDevices();
220
- this.scanForBrowserDevice(existingDevices);
221
286
  this.logger.info('Browser extension auto-discovery started');
222
287
  }
223
288
  catch (err) {
@@ -226,6 +291,36 @@ export class BrowserRelayAdapter {
226
291
  });
227
292
  }
228
293
  }
294
+ /**
295
+ * Handle `instance_connected` from BrowserProxy — adopt as the relay
296
+ * target if we don't already have one (or if the existing one is gone).
297
+ *
298
+ * @param evt - Connected instance info
299
+ */
300
+ onProxyInstanceConnected(evt) {
301
+ if (this.extensionDeviceId === evt.instanceId)
302
+ return;
303
+ this.extensionDeviceId = evt.instanceId;
304
+ this.logger.info('Browser extension auto-discovered (proxy event)', {
305
+ instanceId: evt.instanceId,
306
+ instanceName: evt.instanceName,
307
+ });
308
+ }
309
+ /**
310
+ * Handle `instance_disconnected` from BrowserProxy — clear the relay
311
+ * target only if it matches the disconnecting instance.
312
+ *
313
+ * @param evt - Disconnected instance info
314
+ */
315
+ onProxyInstanceDisconnected(evt) {
316
+ if (this.extensionDeviceId !== evt.instanceId)
317
+ return;
318
+ this.logger.info('Browser extension disconnected (proxy event), clearing relay target', {
319
+ instanceId: evt.instanceId,
320
+ instanceName: evt.instanceName,
321
+ });
322
+ this.extensionDeviceId = null;
323
+ }
229
324
  /**
230
325
  * Stop listening for device events.
231
326
  */
@@ -247,6 +342,28 @@ export class BrowserRelayAdapter {
247
342
  catch {
248
343
  // CloudSync may already be destroyed
249
344
  }
345
+ // Unsubscribe from BrowserProxy events (best-effort dynamic import to
346
+ // match the subscribe path; if the proxy module already collapsed
347
+ // during shutdown, the listeners die with it anyway).
348
+ void (async () => {
349
+ try {
350
+ const { BrowserProxyService } = await import('./browser-proxy.service.js');
351
+ const proxy = BrowserProxyService.getInstance();
352
+ if (this.boundProxyConnectedHandler) {
353
+ proxy.events.removeListener('instance_connected', this.boundProxyConnectedHandler);
354
+ }
355
+ if (this.boundProxyDisconnectedHandler) {
356
+ proxy.events.removeListener('instance_disconnected', this.boundProxyDisconnectedHandler);
357
+ }
358
+ }
359
+ catch {
360
+ // proxy may already be destroyed
361
+ }
362
+ finally {
363
+ this.boundProxyConnectedHandler = null;
364
+ this.boundProxyDisconnectedHandler = null;
365
+ }
366
+ })();
250
367
  this.boundDeviceOnlineHandler = null;
251
368
  this.boundDeviceOfflineHandler = null;
252
369
  this.boundDevicesUpdatedHandler = null;
@@ -1 +1 @@
1
- {"version":3,"file":"browser-relay-adapter.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/browser/browser-relay-adapter.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser-relay-adapter.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/browser/browser-relay-adapter.service.ts"],"names":[],"mappings":"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"}
@@ -22,7 +22,24 @@ export interface PersistedCloudConfig {
22
22
  connectedAt: string;
23
23
  /** Refresh token for auto-renewing expired access tokens */
24
24
  refreshToken?: string;
25
+ /**
26
+ * Relay-signed access JWT (distinct from the access `token`). Persisted so
27
+ * the BrowserProxy can re-register on backend restart without waiting for a
28
+ * fresh validate round-trip. Never the Google/Supabase token — see the
29
+ * RELAY-TOKEN-TYPE invariant in the long-term browser-relay fix design.
30
+ */
31
+ relayToken?: string;
25
32
  }
33
+ /**
34
+ * Decode the `exp` (expiry, seconds since epoch) claim from a JWT without
35
+ * verifying its signature. Returns null when the token is malformed or has
36
+ * no `exp`. Used to schedule proactive relay-token refresh before expiry so
37
+ * a continuously-open relay socket never carries an expired token.
38
+ *
39
+ * @param token - JWT string (may be null)
40
+ * @returns The `exp` claim in seconds, or null when unavailable
41
+ */
42
+ export declare function decodeJwtExp(token: string | null): number | null;
26
43
  /** Summary of a premium template returned by the cloud listing endpoint. */
27
44
  export interface CloudTemplateSummary {
28
45
  /** Unique template identifier */
@@ -116,6 +133,19 @@ export declare class CloudClientService {
116
133
  private refreshInProgress;
117
134
  /** Callbacks invoked when the access token is refreshed */
118
135
  private tokenRefreshCallbacks;
136
+ /**
137
+ * Relay-signed access JWT used by BrowserProxyService to register with the
138
+ * Cloud Relay as role 'agent'. First-class, stored credential — distinct
139
+ * from the access `token` and decoupled from socket lifetime. NEVER the
140
+ * Google/Supabase access token (RELAY-TOKEN-TYPE invariant).
141
+ */
142
+ private relayToken;
143
+ /** Parsed `exp` (seconds) of {@link relayToken}, for proactive refresh. */
144
+ private relayTokenExp;
145
+ /** Timer for proactive relay-token refresh (fires SKEW before exp). */
146
+ private relayRefreshTimer;
147
+ /** Callbacks invoked when the relay token is refreshed (distinct channel). */
148
+ private relayTokenRefreshCallbacks;
119
149
  private constructor();
120
150
  /**
121
151
  * Get the singleton instance.
@@ -277,6 +307,39 @@ export declare class CloudClientService {
277
307
  * @param callback - Function receiving the new access token string
278
308
  */
279
309
  onTokenRefresh(callback: (newToken: string) => void): void;
310
+ /**
311
+ * Get the current relay-signed token (for BrowserProxyService registration).
312
+ *
313
+ * This is DISTINCT from {@link getToken} (the access/HTTP token). The
314
+ * BrowserProxy must register with the relay token, never the access token —
315
+ * see the RELAY-TOKEN-TYPE invariant. Returns null until a relay token has
316
+ * been minted by the Cloud validate/refresh endpoint.
317
+ *
318
+ * @returns Current relay token or null
319
+ */
320
+ getRelayToken(): string | null;
321
+ /**
322
+ * Register a callback invoked whenever the relay token is refreshed.
323
+ *
324
+ * Distinct subscription channel from {@link onTokenRefresh}: subscribers
325
+ * here (BrowserProxyService) receive the relay-signed token, never the
326
+ * access token. Decoupling the channels prevents the proxy from ever being
327
+ * fed the wrong token type on refresh.
328
+ *
329
+ * @param callback - Function receiving the new relay token string
330
+ */
331
+ onRelayTokenRefresh(callback: (newRelayToken: string) => void): void;
332
+ /**
333
+ * Store a freshly-minted relay token as a first-class credential, parse its
334
+ * expiry, schedule proactive refresh, persist it, and notify subscribers.
335
+ *
336
+ * Centralizes relay-token handling so every code path that obtains one
337
+ * (connect, refresh, validate) treats it identically. Persistence is
338
+ * fire-and-forget; a persist failure must not break the refresh chain.
339
+ *
340
+ * @param relayToken - The relay-signed access JWT from Cloud
341
+ */
342
+ private storeRelayToken;
280
343
  /**
281
344
  * Attempt to refresh the access token using the stored refresh token.
282
345
  *
@@ -295,6 +358,18 @@ export declare class CloudClientService {
295
358
  * @param token - JWT access token to extract expiry from
296
359
  */
297
360
  private scheduleTokenRefresh;
361
+ /**
362
+ * Schedule a proactive relay-token refresh `RELAY_REFRESH_SKEW_S` seconds
363
+ * before the relay token's real `exp`.
364
+ *
365
+ * The relay token (~60min TTL) is decoupled from the access token, so it
366
+ * needs its own refresh schedule. When the timer fires it calls
367
+ * {@link fetchRelayTokenFromValidate}, which re-mints a relay token and
368
+ * stores it (re-arming this timer). This keeps a continuously-open relay
369
+ * socket from ever carrying an expired token — the DURABLE-CREDENTIAL
370
+ * invariant. Driven by the JWT `exp` claim, not a wall-clock heuristic.
371
+ */
372
+ private scheduleRelayTokenRefresh;
298
373
  /**
299
374
  * Fetch relay token by calling Cloud validate endpoint.
300
375
  * Used after connectLocal() to get a Cloud-signed relay JWT
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-client.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/cloud-client.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cloud-client.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/cloud-client.service.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,29 @@ import * as path from 'path';
15
15
  import { readFile, writeFile, mkdir, unlink } from 'fs/promises';
16
16
  import { LoggerService } from '../core/logger.service.js';
17
17
  import { CLOUD_CONSTANTS, AUTH_CONSTANTS, } from '../../constants.js';
18
+ /**
19
+ * Decode the `exp` (expiry, seconds since epoch) claim from a JWT without
20
+ * verifying its signature. Returns null when the token is malformed or has
21
+ * no `exp`. Used to schedule proactive relay-token refresh before expiry so
22
+ * a continuously-open relay socket never carries an expired token.
23
+ *
24
+ * @param token - JWT string (may be null)
25
+ * @returns The `exp` claim in seconds, or null when unavailable
26
+ */
27
+ export function decodeJwtExp(token) {
28
+ if (!token)
29
+ return null;
30
+ try {
31
+ const parts = token.split('.');
32
+ if (parts.length !== 3)
33
+ return null;
34
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
35
+ return typeof payload.exp === 'number' ? payload.exp : null;
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
18
41
  // ---------------------------------------------------------------------------
19
42
  // Service
20
43
  // ---------------------------------------------------------------------------
@@ -46,6 +69,19 @@ export class CloudClientService {
46
69
  refreshInProgress = false;
47
70
  /** Callbacks invoked when the access token is refreshed */
48
71
  tokenRefreshCallbacks = [];
72
+ /**
73
+ * Relay-signed access JWT used by BrowserProxyService to register with the
74
+ * Cloud Relay as role 'agent'. First-class, stored credential — distinct
75
+ * from the access `token` and decoupled from socket lifetime. NEVER the
76
+ * Google/Supabase access token (RELAY-TOKEN-TYPE invariant).
77
+ */
78
+ relayToken = null;
79
+ /** Parsed `exp` (seconds) of {@link relayToken}, for proactive refresh. */
80
+ relayTokenExp = null;
81
+ /** Timer for proactive relay-token refresh (fires SKEW before exp). */
82
+ relayRefreshTimer = null;
83
+ /** Callbacks invoked when the relay token is refreshed (distinct channel). */
84
+ relayTokenRefreshCallbacks = [];
49
85
  constructor() {
50
86
  this.logger = LoggerService.getInstance().createComponentLogger('CloudClientService');
51
87
  }
@@ -115,13 +151,12 @@ export class CloudClientService {
115
151
  this.tier = resolvedTier || CLOUD_CONSTANTS.TIERS.FREE;
116
152
  this.connectionStatus = CLOUD_CONSTANTS.CONNECTION_STATUS.CONNECTED;
117
153
  this.lastSyncAt = new Date().toISOString();
118
- // Use relay token from Cloud validate response for BrowserProxyService
154
+ // Use relay token from Cloud validate response for BrowserProxyService.
155
+ // Store it as a first-class credential (not a transient callback arg) so
156
+ // it can be persisted, refreshed proactively, and re-fed on restart.
119
157
  if (data.data?.relayToken) {
120
158
  this.logger.info('Relay token received from Cloud');
121
- // Notify BrowserProxyService via token refresh callbacks
122
- for (const callback of this.tokenRefreshCallbacks) {
123
- callback(data.data.relayToken);
124
- }
159
+ this.storeRelayToken(data.data.relayToken);
125
160
  }
126
161
  this.logger.info('Connected to CrewlyAI Cloud', { tier: this.tier, hasRelayToken: !!data.data?.relayToken });
127
162
  // Schedule proactive token refresh
@@ -183,10 +218,16 @@ export class CloudClientService {
183
218
  this.connectionStatus = CLOUD_CONSTANTS.CONNECTION_STATUS.DISCONNECTED;
184
219
  this.tier = CLOUD_CONSTANTS.TIERS.FREE;
185
220
  this.lastSyncAt = null;
221
+ this.relayToken = null;
222
+ this.relayTokenExp = null;
186
223
  if (this.refreshTimer) {
187
224
  clearTimeout(this.refreshTimer);
188
225
  this.refreshTimer = null;
189
226
  }
227
+ if (this.relayRefreshTimer) {
228
+ clearTimeout(this.relayRefreshTimer);
229
+ this.relayRefreshTimer = null;
230
+ }
190
231
  // Remove persisted config
191
232
  this.removePersistedConfig().catch((err) => {
192
233
  this.logger.warn('Failed to remove persisted cloud config (non-fatal)', {
@@ -212,6 +253,16 @@ export class CloudClientService {
212
253
  const data = await readFile(CloudClientService.getConfigPath(), 'utf-8');
213
254
  const config = JSON.parse(data);
214
255
  if (config.cloudUrl && config.token && config.tier) {
256
+ // Restore the persisted relay token immediately so BrowserProxy can
257
+ // re-register on restart without waiting for a fresh validate
258
+ // round-trip. fetchRelayTokenFromValidate (kicked off by connectLocal)
259
+ // will refresh it shortly after. Do not broadcast here — connect/
260
+ // connectLocal own the wiring of subscribers; this just primes state.
261
+ if (config.relayToken) {
262
+ this.relayToken = config.relayToken;
263
+ this.relayTokenExp = decodeJwtExp(config.relayToken);
264
+ this.scheduleRelayTokenRefresh();
265
+ }
215
266
  return config;
216
267
  }
217
268
  return null;
@@ -232,6 +283,7 @@ export class CloudClientService {
232
283
  tier: this.tier,
233
284
  connectedAt: new Date().toISOString(),
234
285
  ...(this.refreshToken && { refreshToken: this.refreshToken }),
286
+ ...(this.relayToken && { relayToken: this.relayToken }),
235
287
  };
236
288
  const configPath = CloudClientService.getConfigPath();
237
289
  await mkdir(path.dirname(configPath), { recursive: true });
@@ -454,6 +506,60 @@ export class CloudClientService {
454
506
  onTokenRefresh(callback) {
455
507
  this.tokenRefreshCallbacks.push(callback);
456
508
  }
509
+ /**
510
+ * Get the current relay-signed token (for BrowserProxyService registration).
511
+ *
512
+ * This is DISTINCT from {@link getToken} (the access/HTTP token). The
513
+ * BrowserProxy must register with the relay token, never the access token —
514
+ * see the RELAY-TOKEN-TYPE invariant. Returns null until a relay token has
515
+ * been minted by the Cloud validate/refresh endpoint.
516
+ *
517
+ * @returns Current relay token or null
518
+ */
519
+ getRelayToken() {
520
+ return this.relayToken;
521
+ }
522
+ /**
523
+ * Register a callback invoked whenever the relay token is refreshed.
524
+ *
525
+ * Distinct subscription channel from {@link onTokenRefresh}: subscribers
526
+ * here (BrowserProxyService) receive the relay-signed token, never the
527
+ * access token. Decoupling the channels prevents the proxy from ever being
528
+ * fed the wrong token type on refresh.
529
+ *
530
+ * @param callback - Function receiving the new relay token string
531
+ */
532
+ onRelayTokenRefresh(callback) {
533
+ this.relayTokenRefreshCallbacks.push(callback);
534
+ }
535
+ /**
536
+ * Store a freshly-minted relay token as a first-class credential, parse its
537
+ * expiry, schedule proactive refresh, persist it, and notify subscribers.
538
+ *
539
+ * Centralizes relay-token handling so every code path that obtains one
540
+ * (connect, refresh, validate) treats it identically. Persistence is
541
+ * fire-and-forget; a persist failure must not break the refresh chain.
542
+ *
543
+ * @param relayToken - The relay-signed access JWT from Cloud
544
+ */
545
+ storeRelayToken(relayToken) {
546
+ this.relayToken = relayToken;
547
+ this.relayTokenExp = decodeJwtExp(relayToken);
548
+ // Schedule proactive refresh before this relay token's real exp so a
549
+ // continuously-open relay socket never carries an expired token.
550
+ this.scheduleRelayTokenRefresh();
551
+ // Persist alongside the access token for restart resilience.
552
+ this.persistConfig().catch(() => { });
553
+ // Notify subscribers (BrowserProxyService) on the relay-token channel.
554
+ for (const callback of this.relayTokenRefreshCallbacks) {
555
+ try {
556
+ callback(relayToken);
557
+ }
558
+ catch {
559
+ // Non-fatal — one bad callback must not break the refresh chain.
560
+ }
561
+ }
562
+ }
457
563
  /**
458
564
  * Attempt to refresh the access token using the stored refresh token.
459
565
  *
@@ -551,17 +657,27 @@ export class CloudClientService {
551
657
  catch {
552
658
  // CloudSyncService may not be available — non-fatal
553
659
  }
554
- // Notify token refresh subscribers (e.g. BrowserProxyService)
555
- // Use relay token from Cloud refresh if available, otherwise use access token
556
- const tokenForCallbacks = relayToken || newAccessToken;
660
+ // Notify access-token refresh subscribers with the ACCESS token only.
661
+ // The relay token has its own channel (storeRelayToken below) never
662
+ // substitute the access token for the relay token (RELAY-TOKEN-TYPE
663
+ // invariant). Subscribers on this channel that need the relay token
664
+ // should subscribe via onRelayTokenRefresh instead.
557
665
  for (const callback of this.tokenRefreshCallbacks) {
558
666
  try {
559
- callback(tokenForCallbacks);
667
+ callback(newAccessToken);
560
668
  }
561
669
  catch {
562
670
  // Non-fatal — don't let one bad callback break the refresh chain
563
671
  }
564
672
  }
673
+ // Store the relay token as a first-class credential when the refresh
674
+ // returned one. If none was returned, keep the existing stored relay
675
+ // token rather than clobbering it with the access token — a continuing
676
+ // socket keeps working on its still-valid relay token until its own
677
+ // proactive refresh fires.
678
+ if (relayToken) {
679
+ this.storeRelayToken(relayToken);
680
+ }
565
681
  this.logger.info('Access token auto-refreshed successfully', { hasRelayToken: !!relayToken });
566
682
  return true;
567
683
  }
@@ -623,6 +739,44 @@ export class CloudClientService {
623
739
  this.logger.debug('Could not schedule token refresh (non-fatal)');
624
740
  }
625
741
  }
742
+ /**
743
+ * Schedule a proactive relay-token refresh `RELAY_REFRESH_SKEW_S` seconds
744
+ * before the relay token's real `exp`.
745
+ *
746
+ * The relay token (~60min TTL) is decoupled from the access token, so it
747
+ * needs its own refresh schedule. When the timer fires it calls
748
+ * {@link fetchRelayTokenFromValidate}, which re-mints a relay token and
749
+ * stores it (re-arming this timer). This keeps a continuously-open relay
750
+ * socket from ever carrying an expired token — the DURABLE-CREDENTIAL
751
+ * invariant. Driven by the JWT `exp` claim, not a wall-clock heuristic.
752
+ */
753
+ scheduleRelayTokenRefresh() {
754
+ if (this.relayRefreshTimer) {
755
+ clearTimeout(this.relayRefreshTimer);
756
+ this.relayRefreshTimer = null;
757
+ }
758
+ if (!this.relayTokenExp)
759
+ return;
760
+ const now = Math.floor(Date.now() / 1000);
761
+ // Refresh 5 minutes (300s) before expiry — same skew as the access token.
762
+ const refreshAt = this.relayTokenExp - 300;
763
+ const delayMs = Math.max(0, (refreshAt - now) * 1000);
764
+ if (delayMs <= 0) {
765
+ // Already near expiry — refresh immediately.
766
+ this.logger.info('Relay token near expiry, refreshing immediately');
767
+ this.fetchRelayTokenFromValidate().catch(() => { });
768
+ return;
769
+ }
770
+ this.relayRefreshTimer = setTimeout(() => {
771
+ this.logger.info('Proactive relay-token refresh triggered');
772
+ this.fetchRelayTokenFromValidate().catch(() => { });
773
+ }, delayMs);
774
+ // Unref so the timer doesn't keep the process alive.
775
+ if (this.relayRefreshTimer.unref)
776
+ this.relayRefreshTimer.unref();
777
+ const minutesUntilRefresh = Math.round(delayMs / 60_000);
778
+ this.logger.debug('Relay token refresh scheduled', { minutesUntilRefresh });
779
+ }
626
780
  // -------------------------------------------------------------------------
627
781
  // Private helpers
628
782
  // -------------------------------------------------------------------------
@@ -652,9 +806,7 @@ export class CloudClientService {
652
806
  const data = await response.json();
653
807
  if (data.data?.relayToken) {
654
808
  this.logger.info('Relay token received from Cloud validate (after connectLocal)');
655
- for (const callback of this.tokenRefreshCallbacks) {
656
- callback(data.data.relayToken);
657
- }
809
+ this.storeRelayToken(data.data.relayToken);
658
810
  }
659
811
  }
660
812
  /**