crewly 1.5.11 → 1.5.12

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 (172) hide show
  1. package/config/constants.ts +3 -3
  2. package/config/orchestrator_tasks/prompts/orchestrator-prompt.md +73 -0
  3. package/config/roles/architect/prompt.md +9 -0
  4. package/config/roles/backend-developer/prompt.md +9 -0
  5. package/config/roles/content-strategist/prompt.md +10 -0
  6. package/config/roles/designer/prompt.md +9 -0
  7. package/config/roles/developer/prompt.md +9 -0
  8. package/config/roles/frontend-developer/prompt.md +9 -0
  9. package/config/roles/fullstack-dev/prompt.md +9 -0
  10. package/config/roles/generalist/prompt.md +9 -0
  11. package/config/roles/ops/prompt.md +9 -0
  12. package/config/roles/product-manager/prompt.md +9 -0
  13. package/config/roles/qa/prompt.md +9 -0
  14. package/config/roles/qa-engineer/prompt.md +9 -0
  15. package/config/roles/researcher/prompt.md +9 -0
  16. package/config/roles/sales/prompt.md +9 -0
  17. package/config/roles/support/prompt.md +9 -0
  18. package/config/roles/team-leader/prompt.md +11 -0
  19. package/config/roles/tpm/prompt.md +9 -0
  20. package/config/roles/ux-designer/prompt.md +9 -0
  21. package/config/skills/agent/core/block-task/execute.sh +3 -1
  22. package/config/skills/agent/core/pipe-to-sink/execute.sh +41 -0
  23. package/config/skills/agent/core/read-task/execute.sh +3 -1
  24. package/config/skills/agent/core/report-progress/execute.sh +3 -1
  25. package/config/skills/agent/screenshot-compare/SKILL.md +75 -0
  26. package/config/skills/agent/screenshot-compare/execute.sh +182 -0
  27. package/config/skills/agent/screenshot-compare/skill.json +10 -0
  28. package/config/skills/agent/xiaoyuzhoufm-transcript/SKILL.md +85 -0
  29. package/config/skills/agent/xiaoyuzhoufm-transcript/execute.sh +306 -0
  30. package/config/skills/agent/xiaoyuzhoufm-transcript/skill.json +10 -0
  31. package/config/skills/orchestrator/cancel-cron/SKILL.md +44 -0
  32. package/config/skills/orchestrator/create-cron/SKILL.md +58 -0
  33. package/config/skills/orchestrator/list-cron/SKILL.md +51 -0
  34. package/config/skills/orchestrator/update-cron/SKILL.md +52 -0
  35. package/dist/backend/backend/src/constants.d.ts +7 -4
  36. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  37. package/dist/backend/backend/src/constants.js +6 -3
  38. package/dist/backend/backend/src/constants.js.map +1 -1
  39. package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts +21 -2
  40. package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts.map +1 -1
  41. package/dist/backend/backend/src/controllers/browser/browser.controller.js +167 -29
  42. package/dist/backend/backend/src/controllers/browser/browser.controller.js.map +1 -1
  43. package/dist/backend/backend/src/controllers/browser/browser.routes.d.ts +1 -1
  44. package/dist/backend/backend/src/controllers/browser/browser.routes.d.ts.map +1 -1
  45. package/dist/backend/backend/src/controllers/browser/browser.routes.js +7 -3
  46. package/dist/backend/backend/src/controllers/browser/browser.routes.js.map +1 -1
  47. package/dist/backend/backend/src/controllers/data/data.controller.d.ts +47 -0
  48. package/dist/backend/backend/src/controllers/data/data.controller.d.ts.map +1 -0
  49. package/dist/backend/backend/src/controllers/data/data.controller.js +201 -0
  50. package/dist/backend/backend/src/controllers/data/data.controller.js.map +1 -0
  51. package/dist/backend/backend/src/controllers/data/data.routes.d.ts +18 -0
  52. package/dist/backend/backend/src/controllers/data/data.routes.d.ts.map +1 -0
  53. package/dist/backend/backend/src/controllers/data/data.routes.js +44 -0
  54. package/dist/backend/backend/src/controllers/data/data.routes.js.map +1 -0
  55. package/dist/backend/backend/src/controllers/monitoring/token-usage.controller.d.ts +3 -2
  56. package/dist/backend/backend/src/controllers/monitoring/token-usage.controller.d.ts.map +1 -1
  57. package/dist/backend/backend/src/controllers/monitoring/token-usage.controller.js +5 -3
  58. package/dist/backend/backend/src/controllers/monitoring/token-usage.controller.js.map +1 -1
  59. package/dist/backend/backend/src/controllers/system/cron-task.controller.d.ts +4 -0
  60. package/dist/backend/backend/src/controllers/system/cron-task.controller.d.ts.map +1 -1
  61. package/dist/backend/backend/src/controllers/system/cron-task.controller.js +20 -0
  62. package/dist/backend/backend/src/controllers/system/cron-task.controller.js.map +1 -1
  63. package/dist/backend/backend/src/controllers/task-management/task-management.controller.d.ts.map +1 -1
  64. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js +18 -0
  65. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js.map +1 -1
  66. package/dist/backend/backend/src/controllers/team/team-export.controller.d.ts +32 -0
  67. package/dist/backend/backend/src/controllers/team/team-export.controller.d.ts.map +1 -0
  68. package/dist/backend/backend/src/controllers/team/team-export.controller.js +61 -0
  69. package/dist/backend/backend/src/controllers/team/team-export.controller.js.map +1 -0
  70. package/dist/backend/backend/src/controllers/team/team.routes.d.ts.map +1 -1
  71. package/dist/backend/backend/src/controllers/team/team.routes.js +7 -0
  72. package/dist/backend/backend/src/controllers/team/team.routes.js.map +1 -1
  73. package/dist/backend/backend/src/index.d.ts.map +1 -1
  74. package/dist/backend/backend/src/index.js +37 -7
  75. package/dist/backend/backend/src/index.js.map +1 -1
  76. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  77. package/dist/backend/backend/src/routes/api.routes.js +4 -1
  78. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  79. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  80. package/dist/backend/backend/src/services/agent/agent-registration.service.js +6 -2
  81. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  82. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  83. package/dist/backend/backend/src/services/agent/idle-detection.service.js +17 -2
  84. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  85. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts +1 -1
  86. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js +2 -2
  87. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
  88. package/dist/backend/backend/src/services/agent/task-planning.service.d.ts +134 -0
  89. package/dist/backend/backend/src/services/agent/task-planning.service.d.ts.map +1 -0
  90. package/dist/backend/backend/src/services/agent/task-planning.service.js +291 -0
  91. package/dist/backend/backend/src/services/agent/task-planning.service.js.map +1 -0
  92. package/dist/backend/backend/src/services/ai/prompt-modules/communication.module.d.ts.map +1 -1
  93. package/dist/backend/backend/src/services/ai/prompt-modules/communication.module.js +8 -0
  94. package/dist/backend/backend/src/services/ai/prompt-modules/communication.module.js.map +1 -1
  95. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  96. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +3 -3
  97. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  98. package/dist/backend/backend/src/services/browser/browser-bridge.service.d.ts +13 -9
  99. package/dist/backend/backend/src/services/browser/browser-bridge.service.d.ts.map +1 -1
  100. package/dist/backend/backend/src/services/browser/browser-bridge.service.js +44 -12
  101. package/dist/backend/backend/src/services/browser/browser-bridge.service.js.map +1 -1
  102. package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts +176 -0
  103. package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts.map +1 -0
  104. package/dist/backend/backend/src/services/browser/browser-proxy.service.js +441 -0
  105. package/dist/backend/backend/src/services/browser/browser-proxy.service.js.map +1 -0
  106. package/dist/backend/backend/src/services/browser/browser-relay-adapter.service.d.ts +162 -0
  107. package/dist/backend/backend/src/services/browser/browser-relay-adapter.service.d.ts.map +1 -0
  108. package/dist/backend/backend/src/services/browser/browser-relay-adapter.service.js +350 -0
  109. package/dist/backend/backend/src/services/browser/browser-relay-adapter.service.js.map +1 -0
  110. package/dist/backend/backend/src/services/cloud/cloud-initializer.d.ts +8 -0
  111. package/dist/backend/backend/src/services/cloud/cloud-initializer.d.ts.map +1 -1
  112. package/dist/backend/backend/src/services/cloud/cloud-initializer.js +27 -0
  113. package/dist/backend/backend/src/services/cloud/cloud-initializer.js.map +1 -1
  114. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  115. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  116. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +2 -0
  117. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  118. package/dist/backend/backend/src/services/core/team-export.service.d.ts +103 -0
  119. package/dist/backend/backend/src/services/core/team-export.service.d.ts.map +1 -0
  120. package/dist/backend/backend/src/services/core/team-export.service.js +182 -0
  121. package/dist/backend/backend/src/services/core/team-export.service.js.map +1 -0
  122. package/dist/backend/backend/src/services/data/data-object-store.service.d.ts +160 -0
  123. package/dist/backend/backend/src/services/data/data-object-store.service.d.ts.map +1 -0
  124. package/dist/backend/backend/src/services/data/data-object-store.service.js +434 -0
  125. package/dist/backend/backend/src/services/data/data-object-store.service.js.map +1 -0
  126. package/dist/backend/backend/src/services/data/data-object.types.d.ts +190 -0
  127. package/dist/backend/backend/src/services/data/data-object.types.d.ts.map +1 -0
  128. package/dist/backend/backend/src/services/data/data-object.types.js +143 -0
  129. package/dist/backend/backend/src/services/data/data-object.types.js.map +1 -0
  130. package/dist/backend/backend/src/services/data/schema-registry.service.d.ts +108 -0
  131. package/dist/backend/backend/src/services/data/schema-registry.service.d.ts.map +1 -0
  132. package/dist/backend/backend/src/services/data/schema-registry.service.js +290 -0
  133. package/dist/backend/backend/src/services/data/schema-registry.service.js.map +1 -0
  134. package/dist/backend/backend/src/services/data/sink-registry.service.d.ts +87 -0
  135. package/dist/backend/backend/src/services/data/sink-registry.service.d.ts.map +1 -0
  136. package/dist/backend/backend/src/services/data/sink-registry.service.js +188 -0
  137. package/dist/backend/backend/src/services/data/sink-registry.service.js.map +1 -0
  138. package/dist/backend/backend/src/services/messaging/message-router.service.d.ts.map +1 -1
  139. package/dist/backend/backend/src/services/messaging/message-router.service.js +7 -0
  140. package/dist/backend/backend/src/services/messaging/message-router.service.js.map +1 -1
  141. package/dist/backend/backend/src/services/monitoring/token-usage.service.d.ts +55 -2
  142. package/dist/backend/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
  143. package/dist/backend/backend/src/services/monitoring/token-usage.service.js +89 -5
  144. package/dist/backend/backend/src/services/monitoring/token-usage.service.js.map +1 -1
  145. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js +1 -1
  146. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js.map +1 -1
  147. package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts +105 -14
  148. package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
  149. package/dist/backend/backend/src/services/workflow/cron-task.service.js +400 -123
  150. package/dist/backend/backend/src/services/workflow/cron-task.service.js.map +1 -1
  151. package/dist/backend/backend/src/types/cron-task.types.d.ts +1 -1
  152. package/dist/backend/backend/src/types/data-object.types.d.ts +117 -0
  153. package/dist/backend/backend/src/types/data-object.types.d.ts.map +1 -0
  154. package/dist/backend/backend/src/types/data-object.types.js +23 -0
  155. package/dist/backend/backend/src/types/data-object.types.js.map +1 -0
  156. package/dist/backend/backend/src/types/settings.types.js +1 -1
  157. package/dist/backend/config/constants.d.ts +3 -3
  158. package/dist/backend/config/constants.js +3 -3
  159. package/dist/backend/config/constants.js.map +1 -1
  160. package/dist/cli/backend/src/constants.d.ts +7 -4
  161. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  162. package/dist/cli/backend/src/constants.js +6 -3
  163. package/dist/cli/backend/src/constants.js.map +1 -1
  164. package/dist/cli/backend/src/types/settings.types.js +1 -1
  165. package/dist/cli/config/constants.d.ts +3 -3
  166. package/dist/cli/config/constants.js +3 -3
  167. package/dist/cli/config/constants.js.map +1 -1
  168. package/frontend/dist/assets/index-371b68d4.css +33 -0
  169. package/frontend/dist/assets/{index-9af2ea40.js → index-506f70da.js} +321 -321
  170. package/frontend/dist/index.html +2 -2
  171. package/package.json +1 -1
  172. package/frontend/dist/assets/index-b19b2478.css +0 -33
@@ -0,0 +1,441 @@
1
+ /**
2
+ * Browser Proxy Service
3
+ *
4
+ * Connects to the Cloud Relay as an 'agent' and provides addressed messaging
5
+ * to browser instances. Any Crewly agent can call browser tools through this
6
+ * service without needing relay pairing.
7
+ *
8
+ * Architecture:
9
+ * Agent → /api/browser/* → BrowserProxyService → Cloud Relay → Extension
10
+ * Extension → Cloud Relay → BrowserProxyService → HTTP response → Agent
11
+ *
12
+ * @module services/browser/browser-proxy.service
13
+ */
14
+ import WebSocket from 'ws';
15
+ import { LoggerService } from '../core/logger.service.js';
16
+ import { BROWSER_BRIDGE_CONSTANTS } from '../../constants.js';
17
+ /** Default relay WebSocket URL. */
18
+ const DEFAULT_RELAY_URL = 'wss://api.crewlyai.com/relay';
19
+ /** Heartbeat interval to keep the relay connection alive (ms). */
20
+ const HEARTBEAT_INTERVAL_MS = 25_000;
21
+ /** Reconnect delay after disconnection (ms). */
22
+ const RECONNECT_DELAY_MS = 5_000;
23
+ /**
24
+ * BrowserProxyService connects to the Cloud Relay and routes browser commands
25
+ * to specific extension instances using addressed messaging (relay_to).
26
+ *
27
+ * Singleton — one relay connection per backend instance.
28
+ */
29
+ export class BrowserProxyService {
30
+ static instance = null;
31
+ logger;
32
+ /** WebSocket connection to cloud relay */
33
+ ws = null;
34
+ /** Current relay session ID (assigned after registration) */
35
+ sessionId = null;
36
+ /** Connection state */
37
+ state = 'disconnected';
38
+ /** Map of commandId → pending command awaiting response */
39
+ pendingCommands = new Map();
40
+ /** Known browser instances from relay browser_list + browser_event */
41
+ instances = new Map();
42
+ /** Command counter for unique IDs */
43
+ commandCounter = 0;
44
+ /** Heartbeat interval timer */
45
+ heartbeatTimer = null;
46
+ /** Reconnect timer */
47
+ reconnectTimer = null;
48
+ /** Auth token for relay registration */
49
+ authToken = null;
50
+ /** Relay WebSocket URL */
51
+ relayUrl = DEFAULT_RELAY_URL;
52
+ /** Whether auto-reconnect is enabled */
53
+ autoReconnect = true;
54
+ constructor() {
55
+ this.logger = LoggerService.getInstance().createComponentLogger('BrowserProxy');
56
+ }
57
+ /**
58
+ * Get the singleton instance.
59
+ *
60
+ * @returns BrowserProxyService instance
61
+ */
62
+ static getInstance() {
63
+ if (!BrowserProxyService.instance) {
64
+ BrowserProxyService.instance = new BrowserProxyService();
65
+ }
66
+ return BrowserProxyService.instance;
67
+ }
68
+ /**
69
+ * Reset singleton instance (for testing).
70
+ */
71
+ static resetInstance() {
72
+ BrowserProxyService.instance = null;
73
+ }
74
+ /**
75
+ * Connect to the cloud relay WebSocket server.
76
+ *
77
+ * Registers as role 'agent' with a pairing code matching the backend identity.
78
+ * After connection, requests the browser instance list and subscribes to events.
79
+ *
80
+ * @param token - JWT auth token for relay registration
81
+ * @param relayUrl - Optional relay URL override
82
+ */
83
+ connect(token, relayUrl) {
84
+ if (this.state !== 'disconnected') {
85
+ this.logger.warn('Already connected or connecting to relay');
86
+ return;
87
+ }
88
+ this.authToken = token;
89
+ if (relayUrl)
90
+ this.relayUrl = relayUrl;
91
+ this.state = 'connecting';
92
+ this.autoReconnect = true;
93
+ this.logger.info('Connecting to cloud relay for browser proxy', {
94
+ url: this.relayUrl,
95
+ });
96
+ this.ws = new WebSocket(this.relayUrl);
97
+ this.ws.on('open', () => {
98
+ this.logger.info('Relay WebSocket connected, registering as agent');
99
+ this.sendRaw({
100
+ type: 'register',
101
+ role: 'agent',
102
+ pairingCode: `backend-proxy-${Date.now()}`,
103
+ token: this.authToken,
104
+ });
105
+ });
106
+ this.ws.on('message', (data) => {
107
+ this.handleMessage(typeof data === 'string' ? data : data.toString('utf8'));
108
+ });
109
+ this.ws.on('close', (code, reason) => {
110
+ this.logger.info('Relay WebSocket closed', { code, reason: reason.toString() });
111
+ this.handleDisconnect();
112
+ });
113
+ this.ws.on('error', (err) => {
114
+ this.logger.error('Relay WebSocket error', { error: err.message });
115
+ this.handleDisconnect();
116
+ });
117
+ }
118
+ /**
119
+ * Disconnect from the cloud relay.
120
+ */
121
+ disconnect() {
122
+ this.autoReconnect = false;
123
+ this.cleanup();
124
+ }
125
+ /**
126
+ * Check if the proxy is connected to the relay and has browser instances available.
127
+ *
128
+ * @returns True if connected and at least one browser instance is known
129
+ */
130
+ isAvailable() {
131
+ return this.state === 'connected' && this.instances.size > 0;
132
+ }
133
+ /**
134
+ * Check if the proxy has a live relay connection (even if no browsers registered yet).
135
+ *
136
+ * @returns True if WebSocket to relay is connected
137
+ */
138
+ isConnected() {
139
+ return this.state === 'connected';
140
+ }
141
+ /**
142
+ * Get the current connection state.
143
+ *
144
+ * @returns Connection state string
145
+ */
146
+ getState() {
147
+ return this.state;
148
+ }
149
+ /**
150
+ * Get the list of known browser instances.
151
+ *
152
+ * @returns Array of browser instance info
153
+ */
154
+ getInstances() {
155
+ return Array.from(this.instances.values());
156
+ }
157
+ /**
158
+ * Send a browser command to a specific instance (or the only connected one).
159
+ *
160
+ * Routes via relay_to addressed messaging. The command is forwarded by the
161
+ * relay server to the matching browser extension, which executes the tool
162
+ * and returns the response via relay_response.
163
+ *
164
+ * @param tool - Tool name (e.g. 'navigate', 'screenshot', 'getTabs')
165
+ * @param params - Tool parameters
166
+ * @param instance - Target instance name or ID (optional if only 1 connected)
167
+ * @param timeoutMs - Command timeout in milliseconds
168
+ * @param agentName - Agent name for extension banner display
169
+ * @returns Response from the Chrome Extension
170
+ * @throws Error if no instances available, target not found, or timeout
171
+ */
172
+ async sendCommand(tool, params, instance, timeoutMs = BROWSER_BRIDGE_CONSTANTS.COMMAND_TIMEOUT_MS, agentName) {
173
+ if (this.state !== 'connected' || !this.ws) {
174
+ throw new Error('Browser proxy not connected to relay');
175
+ }
176
+ // Resolve target instance
177
+ const targetInstance = this.resolveInstance(instance);
178
+ if (!targetInstance) {
179
+ const available = this.getInstances().map((i) => i.instanceName).join(', ');
180
+ throw new Error(instance
181
+ ? `Browser instance "${instance}" not found. Available: ${available || 'none'}`
182
+ : `No browser instances connected`);
183
+ }
184
+ const id = `proxy-${++this.commandCounter}-${Date.now()}`;
185
+ const payload = JSON.stringify({
186
+ id,
187
+ tool,
188
+ params: params ?? {},
189
+ agentName,
190
+ });
191
+ return new Promise((resolve, reject) => {
192
+ const timer = setTimeout(() => {
193
+ this.pendingCommands.delete(id);
194
+ reject(new Error(`Browser command '${tool}' timed out after ${timeoutMs}ms`));
195
+ }, timeoutMs);
196
+ this.pendingCommands.set(id, { resolve, reject, timer });
197
+ this.sendRaw({
198
+ type: 'relay_to',
199
+ targetInstance: targetInstance.instanceName,
200
+ payload,
201
+ });
202
+ this.logger.debug('Command sent via relay_to', {
203
+ id,
204
+ tool,
205
+ target: targetInstance.instanceName,
206
+ });
207
+ });
208
+ }
209
+ // ---------------------------------------------------------------------------
210
+ // Private methods
211
+ // ---------------------------------------------------------------------------
212
+ /**
213
+ * Resolve which browser instance to target.
214
+ *
215
+ * @param instance - Explicit instance name/ID, or undefined for auto-select
216
+ * @returns Matching BrowserInstanceInfo or null
217
+ */
218
+ resolveInstance(instance) {
219
+ if (!instance) {
220
+ // Auto-select: use the only instance if there's exactly one
221
+ if (this.instances.size === 1) {
222
+ return this.instances.values().next().value ?? null;
223
+ }
224
+ return null;
225
+ }
226
+ // Search by name or ID
227
+ for (const info of this.instances.values()) {
228
+ if (info.instanceName === instance || info.instanceId === instance) {
229
+ return info;
230
+ }
231
+ }
232
+ return null;
233
+ }
234
+ /**
235
+ * Handle incoming WebSocket message from the relay.
236
+ *
237
+ * @param raw - Raw JSON string
238
+ */
239
+ handleMessage(raw) {
240
+ let msg;
241
+ try {
242
+ msg = JSON.parse(raw);
243
+ }
244
+ catch {
245
+ this.logger.warn('Non-JSON relay message ignored');
246
+ return;
247
+ }
248
+ const type = msg.type;
249
+ switch (type) {
250
+ case 'registered':
251
+ this.sessionId = msg.sessionId;
252
+ this.state = 'connected';
253
+ this.startHeartbeat();
254
+ this.logger.info('Registered with relay', { sessionId: this.sessionId });
255
+ // Request browser instance list
256
+ this.sendRaw({ type: 'list_browsers' });
257
+ break;
258
+ case 'browser_list':
259
+ this.handleBrowserList(msg.instances);
260
+ break;
261
+ case 'browser_event':
262
+ this.handleBrowserEvent(msg);
263
+ break;
264
+ case 'relay': {
265
+ // Response from a browser instance
266
+ const payload = msg.payload;
267
+ this.handleRelayPayload(payload);
268
+ break;
269
+ }
270
+ case 'heartbeat_ack':
271
+ // Heartbeat acknowledged
272
+ break;
273
+ case 'error':
274
+ this.logger.warn('Relay error', {
275
+ code: msg.code,
276
+ message: msg.message,
277
+ });
278
+ // Resolve any pending command that might be waiting
279
+ if (msg.code === 'BROWSER_NOT_FOUND' || msg.code === 'BROWSER_UNAVAILABLE') {
280
+ // These errors mean the browser isn't reachable
281
+ this.logger.warn('Browser not reachable via relay', { code: msg.code });
282
+ }
283
+ break;
284
+ default:
285
+ this.logger.debug('Unhandled relay message', { type });
286
+ }
287
+ }
288
+ /**
289
+ * Handle browser_list message from relay.
290
+ *
291
+ * @param instances - Array of browser instance info from relay
292
+ */
293
+ handleBrowserList(instances) {
294
+ this.instances.clear();
295
+ for (const inst of instances) {
296
+ this.instances.set(inst.instanceId, inst);
297
+ }
298
+ this.logger.info('Browser instances updated', {
299
+ count: this.instances.size,
300
+ names: instances.map((i) => i.instanceName),
301
+ });
302
+ }
303
+ /**
304
+ * Handle browser_event (connected/disconnected/updated) from relay.
305
+ *
306
+ * @param msg - Event message
307
+ */
308
+ handleBrowserEvent(msg) {
309
+ const event = msg.event;
310
+ const instanceId = msg.instanceId;
311
+ const instanceName = msg.instanceName;
312
+ switch (event) {
313
+ case 'connected':
314
+ this.instances.set(instanceId, {
315
+ instanceId,
316
+ instanceName,
317
+ sessionId: '', // Will be populated by next list_browsers
318
+ });
319
+ this.logger.info('Browser instance connected', { instanceId, instanceName });
320
+ // Refresh full list to get sessionId
321
+ this.sendRaw({ type: 'list_browsers' });
322
+ break;
323
+ case 'disconnected':
324
+ this.instances.delete(instanceId);
325
+ this.logger.info('Browser instance disconnected', { instanceId, instanceName });
326
+ break;
327
+ case 'updated':
328
+ // Update name
329
+ const existing = this.instances.get(instanceId);
330
+ if (existing) {
331
+ existing.instanceName = instanceName;
332
+ }
333
+ break;
334
+ }
335
+ }
336
+ /**
337
+ * Handle a relay payload (response from a browser instance).
338
+ *
339
+ * @param payload - JSON string containing the command response
340
+ */
341
+ handleRelayPayload(payload) {
342
+ let response;
343
+ try {
344
+ response = JSON.parse(payload);
345
+ }
346
+ catch {
347
+ this.logger.warn('Failed to parse relay payload as command response');
348
+ return;
349
+ }
350
+ if (!response.id)
351
+ return;
352
+ const pending = this.pendingCommands.get(response.id);
353
+ if (pending) {
354
+ clearTimeout(pending.timer);
355
+ this.pendingCommands.delete(response.id);
356
+ pending.resolve(response);
357
+ this.logger.debug('Command response received', {
358
+ id: response.id,
359
+ success: response.success,
360
+ });
361
+ }
362
+ }
363
+ /**
364
+ * Send a raw JSON message to the relay WebSocket.
365
+ *
366
+ * @param msg - Message object to serialize and send
367
+ */
368
+ sendRaw(msg) {
369
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
370
+ this.ws.send(JSON.stringify(msg));
371
+ }
372
+ }
373
+ /**
374
+ * Start heartbeat interval to keep relay connection alive.
375
+ */
376
+ startHeartbeat() {
377
+ this.stopHeartbeat();
378
+ this.heartbeatTimer = setInterval(() => {
379
+ this.sendRaw({ type: 'heartbeat' });
380
+ }, HEARTBEAT_INTERVAL_MS);
381
+ }
382
+ /**
383
+ * Stop heartbeat interval.
384
+ */
385
+ stopHeartbeat() {
386
+ if (this.heartbeatTimer) {
387
+ clearInterval(this.heartbeatTimer);
388
+ this.heartbeatTimer = null;
389
+ }
390
+ }
391
+ /**
392
+ * Handle disconnection — clean up and optionally reconnect.
393
+ */
394
+ handleDisconnect() {
395
+ this.state = 'disconnected';
396
+ this.sessionId = null;
397
+ this.stopHeartbeat();
398
+ // Reject all pending commands
399
+ for (const [id, pending] of this.pendingCommands) {
400
+ clearTimeout(pending.timer);
401
+ pending.reject(new Error('Relay connection lost'));
402
+ this.pendingCommands.delete(id);
403
+ }
404
+ // Auto-reconnect
405
+ if (this.autoReconnect && this.authToken) {
406
+ this.reconnectTimer = setTimeout(() => {
407
+ this.state = 'disconnected'; // Reset for connect()
408
+ this.connect(this.authToken);
409
+ }, RECONNECT_DELAY_MS);
410
+ }
411
+ }
412
+ /**
413
+ * Full cleanup — close WebSocket, stop timers, clear state.
414
+ */
415
+ cleanup() {
416
+ this.stopHeartbeat();
417
+ if (this.reconnectTimer) {
418
+ clearTimeout(this.reconnectTimer);
419
+ this.reconnectTimer = null;
420
+ }
421
+ // Reject all pending commands
422
+ for (const [id, pending] of this.pendingCommands) {
423
+ clearTimeout(pending.timer);
424
+ pending.reject(new Error('Browser proxy shutting down'));
425
+ }
426
+ this.pendingCommands.clear();
427
+ if (this.ws) {
428
+ try {
429
+ this.ws.close(1000, 'Proxy shutdown');
430
+ }
431
+ catch {
432
+ // Ignore close errors
433
+ }
434
+ this.ws = null;
435
+ }
436
+ this.state = 'disconnected';
437
+ this.sessionId = null;
438
+ this.instances.clear();
439
+ }
440
+ }
441
+ //# sourceMappingURL=browser-proxy.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-proxy.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/browser/browser-proxy.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Browser Relay Adapter Service
3
+ *
4
+ * Bridges BrowserBridgeService with CloudSyncService to enable browser
5
+ * commands to flow through the Cloud relay queue when the Chrome Extension
6
+ * is not directly connected via WebSocket.
7
+ *
8
+ * Flow:
9
+ * Agent → BrowserBridgeService → BrowserRelayAdapter → CloudSyncService
10
+ * → Cloud relay queue → Extension polls → executes → sends response
11
+ * → Cloud relay queue → Droplet polls → BrowserRelayAdapter resolves
12
+ *
13
+ * The Extension never sees the droplet IP — all traffic goes through
14
+ * the Cloud relay URL (crewlyai.com).
15
+ *
16
+ * @module services/browser/browser-relay-adapter.service
17
+ */
18
+ import type { BrowserCommandResponse } from './browser-bridge.service.js';
19
+ /**
20
+ * Adapter that routes browser commands through the Cloud relay queue
21
+ * when no direct WebSocket connection exists to the Chrome Extension.
22
+ *
23
+ * Usage:
24
+ * 1. Create adapter with target Extension device ID
25
+ * 2. Register with BrowserBridgeService as relay fallback
26
+ * 3. Handle incoming `browser_response` messages from CloudSyncService
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const adapter = BrowserRelayAdapter.getInstance();
31
+ * adapter.setExtensionDeviceId('ext-device-abc');
32
+ *
33
+ * // Send command via relay
34
+ * const response = await adapter.sendViaRelay('navigate', { url: 'https://example.com' });
35
+ *
36
+ * // Handle incoming responses (called by CloudSyncService message handler)
37
+ * adapter.handleRelayResponse({ id: 'cmd-1', success: true, result: {...} });
38
+ * ```
39
+ */
40
+ export declare class BrowserRelayAdapter {
41
+ private static instance;
42
+ private readonly logger;
43
+ private pendingCommands;
44
+ private commandCounter;
45
+ /** Device ID of the connected Chrome Extension (from Cloud device list) */
46
+ private extensionDeviceId;
47
+ /** Whether auto-discovery is currently active */
48
+ private discoveryActive;
49
+ /** Bound handler references for cleanup */
50
+ private boundDeviceOnlineHandler;
51
+ private boundDeviceOfflineHandler;
52
+ private boundDevicesUpdatedHandler;
53
+ private constructor();
54
+ /**
55
+ * Get the singleton instance.
56
+ *
57
+ * @returns BrowserRelayAdapter instance
58
+ */
59
+ static getInstance(): BrowserRelayAdapter;
60
+ /**
61
+ * Reset singleton instance (for testing).
62
+ */
63
+ static resetInstance(): void;
64
+ /**
65
+ * Set the target Chrome Extension device ID for relay routing.
66
+ * This device ID comes from CloudSyncService device discovery.
67
+ *
68
+ * @param deviceId - Cloud device ID of the Extension
69
+ */
70
+ setExtensionDeviceId(deviceId: string): void;
71
+ /**
72
+ * Get the current Extension device ID.
73
+ *
74
+ * @returns Device ID or null if not set
75
+ */
76
+ getExtensionDeviceId(): string | null;
77
+ /**
78
+ * Whether the relay adapter is ready to send commands.
79
+ * Requires an Extension device ID and an active CloudSyncService.
80
+ *
81
+ * @returns True if relay is available
82
+ */
83
+ isAvailable(): boolean;
84
+ /**
85
+ * Send a browser command to the Chrome Extension via the Cloud relay queue.
86
+ *
87
+ * @param tool - Tool name to execute (e.g., 'navigate', 'screenshot')
88
+ * @param params - Tool parameters
89
+ * @param timeoutMs - Command timeout in milliseconds
90
+ * @param agentName - Optional agent name for display in Extension
91
+ * @returns Response from the Chrome Extension
92
+ * @throws Error if relay is not available or command times out
93
+ */
94
+ sendViaRelay(tool: string, params?: Record<string, unknown>, timeoutMs?: number, agentName?: string): Promise<BrowserCommandResponse>;
95
+ /**
96
+ * Handle an incoming browser_response message from the Cloud relay queue.
97
+ * Called by CloudSyncService message handler when a browser_response arrives.
98
+ *
99
+ * @param response - The browser command response from the Extension
100
+ */
101
+ handleRelayResponse(response: BrowserCommandResponse): void;
102
+ /**
103
+ * Get the number of pending relay commands.
104
+ *
105
+ * @returns Number of commands awaiting response
106
+ */
107
+ getPendingCount(): number;
108
+ /**
109
+ * Start listening for CloudSync device events to auto-discover Chrome
110
+ * Extension devices. When a device with role 'browser' or 'chrome-extension'
111
+ * comes online, automatically sets it as the relay target.
112
+ *
113
+ * Also scans the current device list on startup for already-online extensions.
114
+ */
115
+ startAutoDiscovery(): void;
116
+ /**
117
+ * Stop listening for device events.
118
+ */
119
+ stopAutoDiscovery(): void;
120
+ /**
121
+ * Whether auto-discovery is active.
122
+ *
123
+ * @returns True if listening for device events
124
+ */
125
+ isDiscoveryActive(): boolean;
126
+ /**
127
+ * Check if a SyncDevice represents a Chrome Extension (browser role).
128
+ *
129
+ * @param device - Device to check
130
+ * @returns True if the device has a browser/chrome-extension role
131
+ */
132
+ private isBrowserDevice;
133
+ /**
134
+ * Handle a device coming online — if it's a browser extension, set it as relay target.
135
+ *
136
+ * @param device - The device that came online
137
+ */
138
+ private onDeviceOnline;
139
+ /**
140
+ * Handle a device going offline — if it's our current extension, clear the target.
141
+ *
142
+ * @param device - The device that went offline
143
+ */
144
+ private onDeviceOffline;
145
+ /**
146
+ * Handle devices_updated — scan for browser extensions in the full list.
147
+ *
148
+ * @param devices - Current device list
149
+ */
150
+ private onDevicesUpdated;
151
+ /**
152
+ * Scan a device list for browser extensions and set the first online one as relay target.
153
+ *
154
+ * @param devices - Device list to scan
155
+ */
156
+ private scanForBrowserDevice;
157
+ /**
158
+ * Clean up pending commands and timers.
159
+ */
160
+ cleanup(): void;
161
+ }
162
+ //# sourceMappingURL=browser-relay-adapter.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-relay-adapter.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/browser/browser-relay-adapter.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAkB,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAkB1F;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAoC;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,cAAc,CAAK;IAC3B,2EAA2E;IAC3E,OAAO,CAAC,iBAAiB,CAAuB;IAChD,iDAAiD;IACjD,OAAO,CAAC,eAAe,CAAS;IAChC,2CAA2C;IAC3C,OAAO,CAAC,wBAAwB,CAA+C;IAC/E,OAAO,CAAC,yBAAyB,CAA+C;IAChF,OAAO,CAAC,0BAA0B,CAAkD;IAEpF,OAAO;IAIP;;;;OAIG;IACH,MAAM,CAAC,WAAW,IAAI,mBAAmB;IAOzC;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAO5B;;;;;OAKG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK5C;;;;OAIG;IACH,oBAAoB,IAAI,MAAM,GAAG,IAAI;IAIrC;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAUtB;;;;;;;;;OASG;IACG,YAAY,CAChB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,GAAE,MAAoD,EAC/D,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,sBAAsB,CAAC;IAkClC;;;;;OAKG;IACH,mBAAmB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAsB3D;;;;OAIG;IACH,eAAe,IAAI,MAAM;IAQzB;;;;;;OAMG;IACH,kBAAkB,IAAI,IAAI;IAsC1B;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAyBzB;;;;OAIG;IACH,iBAAiB,IAAI,OAAO;IAI5B;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAMvB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAWtB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAwB5B;;OAEG;IACH,OAAO,IAAI,IAAI;CAShB"}