parse-dashboard-analytics 7.4.2 → 7.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Parse-Dashboard/Authentication.js +9 -4
- package/Parse-Dashboard/CLI/mfa.js +11 -5
- package/Parse-Dashboard/app.js +93 -26
- package/Parse-Dashboard/browser-control/BrowserControlAPI.js +468 -0
- package/Parse-Dashboard/browser-control/BrowserEventStream.js +294 -0
- package/Parse-Dashboard/browser-control/BrowserSessionManager.js +304 -0
- package/Parse-Dashboard/browser-control/README.md +852 -0
- package/Parse-Dashboard/browser-control/ServerOrchestrator.js +334 -0
- package/Parse-Dashboard/browser-control/index.js +27 -0
- package/Parse-Dashboard/browser-control/setup.js +405 -0
- package/Parse-Dashboard/public/bundles/120.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/183.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/19.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/221.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/372.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/448.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/573.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/578.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/584.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/629.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/643.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/647.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/701.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/729.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/75.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/753.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/817.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/844.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/862.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/866.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/874.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/881.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/929.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/950.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/97.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/976.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/dashboard.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/dashboard.bundle.js.LICENSE.txt +27 -33
- package/Parse-Dashboard/public/bundles/login.bundle.js +1 -1
- package/Parse-Dashboard/public/bundles/sprites.svg +294 -154
- package/Parse-Dashboard/server.js +33 -2
- package/README.md +475 -117
- package/package.json +68 -53
|
@@ -0,0 +1,852 @@
|
|
|
1
|
+
# Browser Control API for Parse Dashboard
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Browser Control API is a development-time tool that allows AI agents to interact with Parse Dashboard through an automated browser during feature implementation and debugging. This enables real-time verification without writing test files.
|
|
6
|
+
|
|
7
|
+
**This is NOT a replacement for unit tests or E2E tests** - it's specifically designed for AI agents to validate implementations while actively developing features.
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
### Prerequisites
|
|
12
|
+
|
|
13
|
+
**Requirements:**
|
|
14
|
+
- Node.js 20.19.0 or higher
|
|
15
|
+
|
|
16
|
+
**Note:** MongoDB is automatically started when you run `npm run browser-control` - no manual setup needed!
|
|
17
|
+
|
|
18
|
+
### 1. Start Dashboard with Browser Control
|
|
19
|
+
|
|
20
|
+
When you run the browser-control command, **MongoDB and Parse Server automatically start** alongside the dashboard - zero setup required!
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm run browser-control
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This will:
|
|
27
|
+
1. Download and start MongoDB 8.0.4 (if not already downloaded)
|
|
28
|
+
2. Start Parse Server on port 1337 (connects to the auto-started MongoDB)
|
|
29
|
+
3. Start Dashboard on port 4040
|
|
30
|
+
4. Auto-configure dashboard with a test app pointing to Parse Server
|
|
31
|
+
5. Enable Browser Control API at `/browser-control`
|
|
32
|
+
|
|
33
|
+
Or for visible browser (helpful for debugging):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run browser-control:visible
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Both scripts include webpack watch mode, so code changes are automatically rebuilt. Just reload the page to see updates.
|
|
40
|
+
|
|
41
|
+
**What happens automatically:**
|
|
42
|
+
- MongoDB 8.0.4 is downloaded (on first run) and started automatically
|
|
43
|
+
- Parse Server spawns and connects to the MongoDB instance
|
|
44
|
+
- Dashboard creates a test app called "Browser Control Test App"
|
|
45
|
+
- You can immediately start creating browser sessions and testing
|
|
46
|
+
- Everything stops cleanly when you exit (Ctrl+C)
|
|
47
|
+
|
|
48
|
+
### 2. Wait for Dashboard to be Ready
|
|
49
|
+
|
|
50
|
+
Before creating a browser session, wait for webpack to finish compiling the assets:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
curl http://localhost:4040/browser-control/ready/wait
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Response:
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"ready": true,
|
|
60
|
+
"waited": 6273,
|
|
61
|
+
"webpack": {
|
|
62
|
+
"compiling": false,
|
|
63
|
+
"lastCompilationTime": 1234567890000,
|
|
64
|
+
"lastCompilationDuration": 6152,
|
|
65
|
+
"compileCount": 1
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This is important because webpack runs in watch mode and compiles assets on startup and after file changes. Navigating before compilation completes will result in timeouts.
|
|
71
|
+
|
|
72
|
+
### 3. Create a Browser Session
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
curl -X POST http://localhost:4040/browser-control/session/start \
|
|
76
|
+
-H "Content-Type: application/json" \
|
|
77
|
+
-d '{"headless": true, "startServers": false}'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Response:
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"sessionId": "sess_1234567890_abc123",
|
|
84
|
+
"dashboardUrl": null,
|
|
85
|
+
"parseServerUrl": null
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 4. Navigate to Dashboard
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
curl -X POST http://localhost:4040/browser-control/session/sess_1234567890_abc123/navigate \
|
|
93
|
+
-H "Content-Type: application/json" \
|
|
94
|
+
-d '{"url": "http://localhost:4040/"}'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 5. Take Screenshot
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
curl http://localhost:4040/browser-control/session/sess_1234567890_abc123/screenshot
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 6. Cleanup
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
curl -X DELETE http://localhost:4040/browser-control/session/sess_1234567890_abc123
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## API Reference
|
|
110
|
+
|
|
111
|
+
### Session Management
|
|
112
|
+
|
|
113
|
+
#### Create Session
|
|
114
|
+
**POST** `/browser-control/session/start`
|
|
115
|
+
|
|
116
|
+
Create a new browser session, optionally starting Parse Server and Dashboard.
|
|
117
|
+
|
|
118
|
+
**Request Body:**
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"headless": true, // Optional: Run headless (default: true)
|
|
122
|
+
"width": 1280, // Optional: Browser width (default: 1280)
|
|
123
|
+
"height": 720, // Optional: Browser height (default: 720)
|
|
124
|
+
"slowMo": 0, // Optional: Slow down by N ms (default: 0)
|
|
125
|
+
"startServers": false, // Optional: Start Parse Server + Dashboard (default: false)
|
|
126
|
+
"parseServerOptions": { // Optional: Parse Server config
|
|
127
|
+
"port": 1337,
|
|
128
|
+
"appId": "testAppId",
|
|
129
|
+
"masterKey": "testMasterKey",
|
|
130
|
+
"databaseURI": "mongodb://localhost:27017/test"
|
|
131
|
+
},
|
|
132
|
+
"dashboardOptions": { // Optional: Dashboard config
|
|
133
|
+
"port": 4040,
|
|
134
|
+
"appName": "TestApp"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Response:**
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"sessionId": "sess_1234567890_abc123",
|
|
143
|
+
"dashboardUrl": "http://localhost:4040",
|
|
144
|
+
"parseServerUrl": "http://localhost:1337/parse",
|
|
145
|
+
"servers": {
|
|
146
|
+
"parseServer": { "port": 1337, "appId": "testAppId" },
|
|
147
|
+
"dashboard": { "port": 4040 }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Get Session Status
|
|
153
|
+
**GET** `/browser-control/session/:sessionId/status`
|
|
154
|
+
|
|
155
|
+
**Response:**
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"sessionId": "sess_1234567890_abc123",
|
|
159
|
+
"active": true,
|
|
160
|
+
"pageUrl": "http://localhost:4040/apps",
|
|
161
|
+
"createdAt": 1234567890000,
|
|
162
|
+
"lastActivity": 1234567895000,
|
|
163
|
+
"uptime": 5000
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### Delete Session
|
|
168
|
+
**DELETE** `/browser-control/session/:sessionId`
|
|
169
|
+
|
|
170
|
+
Cleanup and destroy browser session.
|
|
171
|
+
|
|
172
|
+
**Response:**
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"success": true,
|
|
176
|
+
"sessionId": "sess_1234567890_abc123"
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### List All Sessions
|
|
181
|
+
**GET** `/browser-control/sessions`
|
|
182
|
+
|
|
183
|
+
**Response:**
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"sessions": [
|
|
187
|
+
{
|
|
188
|
+
"sessionId": "sess_1234567890_abc123",
|
|
189
|
+
"createdAt": 1234567890000,
|
|
190
|
+
"lastActivity": 1234567895000,
|
|
191
|
+
"uptime": 5000,
|
|
192
|
+
"crashed": false,
|
|
193
|
+
"pageUrl": "http://localhost:4040/apps"
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
"count": 1
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Navigation & Interaction
|
|
201
|
+
|
|
202
|
+
#### Navigate
|
|
203
|
+
**POST** `/browser-control/session/:sessionId/navigate`
|
|
204
|
+
|
|
205
|
+
Navigate to a URL.
|
|
206
|
+
|
|
207
|
+
**Request Body:**
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"url": "http://localhost:4040/apps",
|
|
211
|
+
"waitUntil": "networkidle2", // Optional: load, domcontentloaded, networkidle0, networkidle2
|
|
212
|
+
"timeout": 30000 // Optional: timeout in ms (default: 30000)
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Response:**
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"success": true,
|
|
220
|
+
"currentUrl": "http://localhost:4040/apps"
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Click Element
|
|
225
|
+
**POST** `/browser-control/session/:sessionId/click`
|
|
226
|
+
|
|
227
|
+
Click an element by CSS selector.
|
|
228
|
+
|
|
229
|
+
**Request Body:**
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"selector": "#login-button",
|
|
233
|
+
"timeout": 5000 // Optional: timeout in ms (default: 5000)
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Response:**
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"success": true,
|
|
241
|
+
"selector": "#login-button"
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### Type Text
|
|
246
|
+
**POST** `/browser-control/session/:sessionId/type`
|
|
247
|
+
|
|
248
|
+
Type text into an input field.
|
|
249
|
+
|
|
250
|
+
**Request Body:**
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"selector": "input[name='username']",
|
|
254
|
+
"text": "admin",
|
|
255
|
+
"timeout": 5000, // Optional: timeout in ms (default: 5000)
|
|
256
|
+
"delay": 0 // Optional: delay between keystrokes in ms (default: 0)
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Response:**
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
263
|
+
"success": true,
|
|
264
|
+
"selector": "input[name='username']",
|
|
265
|
+
"text": "admin"
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### Wait for Element
|
|
270
|
+
**POST** `/browser-control/session/:sessionId/wait`
|
|
271
|
+
|
|
272
|
+
Wait for an element to appear.
|
|
273
|
+
|
|
274
|
+
**Request Body:**
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"selector": ".data-browser",
|
|
278
|
+
"timeout": 10000, // Optional: timeout in ms (default: 10000)
|
|
279
|
+
"visible": true // Optional: wait for visibility (default: true)
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Response:**
|
|
284
|
+
```json
|
|
285
|
+
{
|
|
286
|
+
"success": true,
|
|
287
|
+
"selector": ".data-browser",
|
|
288
|
+
"found": true,
|
|
289
|
+
"duration": 342
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
#### Reload Page
|
|
294
|
+
**POST** `/browser-control/session/:sessionId/reload`
|
|
295
|
+
|
|
296
|
+
Reload the current page.
|
|
297
|
+
|
|
298
|
+
**Request Body:**
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"waitUntil": "networkidle2" // Optional: wait condition
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Response:**
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"success": true,
|
|
309
|
+
"currentUrl": "http://localhost:4040/apps"
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Inspection & Debugging
|
|
314
|
+
|
|
315
|
+
#### Take Screenshot
|
|
316
|
+
**GET** `/browser-control/session/:sessionId/screenshot?fullPage=true&encoding=base64`
|
|
317
|
+
|
|
318
|
+
Capture a screenshot of the current page.
|
|
319
|
+
|
|
320
|
+
**Query Parameters:**
|
|
321
|
+
- `fullPage`: Capture full scrollable page (default: `true`)
|
|
322
|
+
- `encoding`: `base64` or `binary` (default: `base64`)
|
|
323
|
+
|
|
324
|
+
**Response:**
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
#### Execute JavaScript
|
|
332
|
+
**POST** `/browser-control/session/:sessionId/evaluate`
|
|
333
|
+
|
|
334
|
+
Execute JavaScript in the page context.
|
|
335
|
+
|
|
336
|
+
> [!WARNING]
|
|
337
|
+
> This endpoint executes arbitrary JavaScript in the browser page context. While the code runs in a sandboxed browser environment (not on the server), it can access anything visible to the page including cookies, localStorage, and DOM content. This is intentional for AI agent debugging but is a powerful capability. The endpoint is protected by the production block - it cannot be enabled when `NODE_ENV=production`.
|
|
338
|
+
|
|
339
|
+
**Request Body:**
|
|
340
|
+
```json
|
|
341
|
+
{
|
|
342
|
+
"script": "document.querySelector('.graph-panel-header')?.offsetHeight"
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Response:**
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"result": 50
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### Query Elements
|
|
354
|
+
**POST** `/browser-control/session/:sessionId/query`
|
|
355
|
+
|
|
356
|
+
Query elements on the page.
|
|
357
|
+
|
|
358
|
+
**Request Body:**
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"selector": ".data-row",
|
|
362
|
+
"multiple": true // Optional: query multiple elements (default: false)
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Response (single):**
|
|
367
|
+
```json
|
|
368
|
+
{
|
|
369
|
+
"element": {
|
|
370
|
+
"text": "Test Object",
|
|
371
|
+
"visible": true,
|
|
372
|
+
"tagName": "DIV",
|
|
373
|
+
"className": "data-row"
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Response (multiple):**
|
|
379
|
+
```json
|
|
380
|
+
{
|
|
381
|
+
"elements": [
|
|
382
|
+
{
|
|
383
|
+
"text": "Object 1",
|
|
384
|
+
"visible": true,
|
|
385
|
+
"tagName": "DIV",
|
|
386
|
+
"className": "data-row"
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
"text": "Object 2",
|
|
390
|
+
"visible": true,
|
|
391
|
+
"tagName": "DIV",
|
|
392
|
+
"className": "data-row"
|
|
393
|
+
}
|
|
394
|
+
],
|
|
395
|
+
"count": 2
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Server Management
|
|
400
|
+
|
|
401
|
+
#### Get Server Status
|
|
402
|
+
**GET** `/browser-control/servers/status`
|
|
403
|
+
|
|
404
|
+
**Response:**
|
|
405
|
+
```json
|
|
406
|
+
{
|
|
407
|
+
"parseServer": {
|
|
408
|
+
"running": true,
|
|
409
|
+
"port": 1337,
|
|
410
|
+
"serverURL": "http://localhost:1337/parse",
|
|
411
|
+
"appId": "testAppId"
|
|
412
|
+
},
|
|
413
|
+
"dashboard": {
|
|
414
|
+
"running": true,
|
|
415
|
+
"port": 4040,
|
|
416
|
+
"url": "http://localhost:4040"
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
#### Stop Servers
|
|
422
|
+
**POST** `/browser-control/servers/stop`
|
|
423
|
+
|
|
424
|
+
Stop Parse Server and Dashboard instances.
|
|
425
|
+
|
|
426
|
+
**Response:**
|
|
427
|
+
```json
|
|
428
|
+
{
|
|
429
|
+
"success": true
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Webpack Readiness
|
|
434
|
+
|
|
435
|
+
#### Check Ready Status
|
|
436
|
+
**GET** `/browser-control/ready`
|
|
437
|
+
|
|
438
|
+
Check if the dashboard is ready (webpack has finished compiling assets).
|
|
439
|
+
|
|
440
|
+
**Response:**
|
|
441
|
+
```json
|
|
442
|
+
{
|
|
443
|
+
"ready": true,
|
|
444
|
+
"webpack": {
|
|
445
|
+
"compiling": false,
|
|
446
|
+
"lastCompilationTime": 1234567890000,
|
|
447
|
+
"lastCompilationDuration": 6152,
|
|
448
|
+
"compileCount": 1,
|
|
449
|
+
"hasErrors": false
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
#### Wait for Ready
|
|
455
|
+
**GET** `/browser-control/ready/wait?timeout=30000`
|
|
456
|
+
|
|
457
|
+
Wait for webpack to finish compiling. This is a blocking endpoint that polls until webpack is idle or timeout is reached.
|
|
458
|
+
|
|
459
|
+
**Query Parameters:**
|
|
460
|
+
- `timeout`: Maximum time to wait in milliseconds (default: 30000)
|
|
461
|
+
|
|
462
|
+
**Response (success):**
|
|
463
|
+
```json
|
|
464
|
+
{
|
|
465
|
+
"ready": true,
|
|
466
|
+
"waited": 6273,
|
|
467
|
+
"webpack": {
|
|
468
|
+
"compiling": false,
|
|
469
|
+
"lastCompilationTime": 1234567890000,
|
|
470
|
+
"lastCompilationDuration": 6152,
|
|
471
|
+
"compileCount": 1
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
**Response (timeout - HTTP 408):**
|
|
477
|
+
```json
|
|
478
|
+
{
|
|
479
|
+
"ready": false,
|
|
480
|
+
"error": "Timeout waiting for webpack to finish compiling",
|
|
481
|
+
"waited": 30000,
|
|
482
|
+
"webpack": {
|
|
483
|
+
"compiling": true,
|
|
484
|
+
"compileCount": 1
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
> [!IMPORTANT]
|
|
490
|
+
> Always call `/ready/wait` before creating browser sessions, especially after:
|
|
491
|
+
> - Starting the dashboard
|
|
492
|
+
> - Editing source files (triggers webpack recompilation)
|
|
493
|
+
>
|
|
494
|
+
> Navigation attempts while webpack is compiling will timeout because the JavaScript bundles are not yet available.
|
|
495
|
+
|
|
496
|
+
### Cleanup
|
|
497
|
+
|
|
498
|
+
#### Cleanup All
|
|
499
|
+
**POST** `/browser-control/cleanup`
|
|
500
|
+
|
|
501
|
+
Cleanup all sessions and stop all servers.
|
|
502
|
+
|
|
503
|
+
**Response:**
|
|
504
|
+
```json
|
|
505
|
+
{
|
|
506
|
+
"success": true
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## WebSocket Event Stream
|
|
511
|
+
|
|
512
|
+
Connect to the WebSocket stream to receive real-time events from the browser.
|
|
513
|
+
|
|
514
|
+
### Connection
|
|
515
|
+
|
|
516
|
+
```
|
|
517
|
+
ws://localhost:4040/browser-control/stream/:sessionId
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Event Types
|
|
521
|
+
|
|
522
|
+
#### Console Messages
|
|
523
|
+
```json
|
|
524
|
+
{
|
|
525
|
+
"type": "console",
|
|
526
|
+
"level": "log",
|
|
527
|
+
"text": "Application loaded",
|
|
528
|
+
"timestamp": 1234567890000
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
#### Errors
|
|
533
|
+
```json
|
|
534
|
+
{
|
|
535
|
+
"type": "error",
|
|
536
|
+
"message": "Uncaught TypeError: Cannot read property 'x' of undefined",
|
|
537
|
+
"stack": "TypeError: Cannot read property 'x' of undefined\n at...",
|
|
538
|
+
"timestamp": 1234567890000
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
#### Network Requests
|
|
543
|
+
```json
|
|
544
|
+
{
|
|
545
|
+
"type": "network",
|
|
546
|
+
"method": "POST",
|
|
547
|
+
"url": "http://localhost:1337/parse/classes/TestClass",
|
|
548
|
+
"status": 200,
|
|
549
|
+
"timestamp": 1234567890000
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
#### Navigation
|
|
554
|
+
```json
|
|
555
|
+
{
|
|
556
|
+
"type": "navigation",
|
|
557
|
+
"url": "http://localhost:4040/apps/testApp/browser",
|
|
558
|
+
"timestamp": 1234567890000
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
#### Session Events
|
|
563
|
+
```json
|
|
564
|
+
{
|
|
565
|
+
"type": "session-event",
|
|
566
|
+
"event": "created",
|
|
567
|
+
"sessionId": "sess_1234567890_abc123",
|
|
568
|
+
"timestamp": 1234567890000
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
## AI Agent Workflow Examples
|
|
573
|
+
|
|
574
|
+
### Example 1: Verify Feature Implementation
|
|
575
|
+
|
|
576
|
+
```javascript
|
|
577
|
+
// AI agent just implemented a delete button feature
|
|
578
|
+
|
|
579
|
+
// 1. Wait for webpack to be ready (important after editing files!)
|
|
580
|
+
await fetch('http://localhost:4040/browser-control/ready/wait');
|
|
581
|
+
|
|
582
|
+
// 2. Create session
|
|
583
|
+
const createRes = await fetch('http://localhost:4040/browser-control/session/start', {
|
|
584
|
+
method: 'POST',
|
|
585
|
+
headers: { 'Content-Type': 'application/json' },
|
|
586
|
+
body: JSON.stringify({ headless: false, startServers: false })
|
|
587
|
+
});
|
|
588
|
+
const { sessionId } = await createRes.json();
|
|
589
|
+
|
|
590
|
+
// 3. Navigate to class browser
|
|
591
|
+
await fetch(`http://localhost:4040/browser-control/session/${sessionId}/navigate`, {
|
|
592
|
+
method: 'POST',
|
|
593
|
+
headers: { 'Content-Type': 'application/json' },
|
|
594
|
+
body: JSON.stringify({ url: 'http://localhost:4040/apps/testApp/browser/TestClass' })
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
// 4. Check if delete button exists
|
|
598
|
+
const queryRes = await fetch(`http://localhost:4040/browser-control/session/${sessionId}/query`, {
|
|
599
|
+
method: 'POST',
|
|
600
|
+
headers: { 'Content-Type': 'application/json' },
|
|
601
|
+
body: JSON.stringify({ selector: '.delete-button' })
|
|
602
|
+
});
|
|
603
|
+
const { element } = await queryRes.json();
|
|
604
|
+
|
|
605
|
+
if (element) {
|
|
606
|
+
console.log('✓ Delete button found!');
|
|
607
|
+
|
|
608
|
+
// 5. Take screenshot for verification
|
|
609
|
+
const screenshotRes = await fetch(
|
|
610
|
+
`http://localhost:4040/browser-control/session/${sessionId}/screenshot`
|
|
611
|
+
);
|
|
612
|
+
const { base64 } = await screenshotRes.json();
|
|
613
|
+
// AI can analyze screenshot
|
|
614
|
+
} else {
|
|
615
|
+
console.log('❌ Delete button not found - need to fix');
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// 6. Cleanup
|
|
619
|
+
await fetch(`http://localhost:4040/browser-control/session/${sessionId}`, {
|
|
620
|
+
method: 'DELETE'
|
|
621
|
+
});
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
### Example 2: Debug UI Issue
|
|
625
|
+
|
|
626
|
+
```javascript
|
|
627
|
+
// AI agent is debugging layout alignment
|
|
628
|
+
|
|
629
|
+
const { sessionId } = await createSession();
|
|
630
|
+
await navigate(sessionId, 'http://localhost:4040/apps/testApp/browser');
|
|
631
|
+
|
|
632
|
+
// Check actual heights
|
|
633
|
+
const heightsRes = await fetch(
|
|
634
|
+
`http://localhost:4040/browser-control/session/${sessionId}/evaluate`,
|
|
635
|
+
{
|
|
636
|
+
method: 'POST',
|
|
637
|
+
headers: { 'Content-Type': 'application/json' },
|
|
638
|
+
body: JSON.stringify({
|
|
639
|
+
script: `({
|
|
640
|
+
graphPanelHeader: document.querySelector('.graph-panel-header')?.offsetHeight,
|
|
641
|
+
infoPanelHeader: document.querySelector('.info-panel-header')?.offsetHeight
|
|
642
|
+
})`
|
|
643
|
+
})
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
const { result } = await heightsRes.json();
|
|
647
|
+
|
|
648
|
+
console.log('Header heights:', result);
|
|
649
|
+
// { graphPanelHeader: 45, infoPanelHeader: 50 }
|
|
650
|
+
|
|
651
|
+
if (result.graphPanelHeader !== result.infoPanelHeader) {
|
|
652
|
+
console.log('❌ Heights misaligned - adjusting CSS...');
|
|
653
|
+
// AI makes CSS changes and checks again
|
|
654
|
+
}
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
### Example 3: Monitor Console Errors
|
|
658
|
+
|
|
659
|
+
```javascript
|
|
660
|
+
// Connect WebSocket to monitor real-time console logs
|
|
661
|
+
const ws = new WebSocket(`ws://localhost:4040/browser-control/stream/${sessionId}`);
|
|
662
|
+
|
|
663
|
+
const errors = [];
|
|
664
|
+
ws.on('message', (data) => {
|
|
665
|
+
const event = JSON.parse(data);
|
|
666
|
+
if (event.type === 'console' && event.level === 'error') {
|
|
667
|
+
errors.push(event.text);
|
|
668
|
+
console.log('⚠️ Console error:', event.text);
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
// Navigate and check for errors
|
|
673
|
+
await navigate(sessionId, 'http://localhost:4040/apps');
|
|
674
|
+
|
|
675
|
+
// Wait for page to load
|
|
676
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
677
|
+
|
|
678
|
+
if (errors.length > 0) {
|
|
679
|
+
console.log(`❌ Found ${errors.length} console errors:`, errors);
|
|
680
|
+
} else {
|
|
681
|
+
console.log('✓ No console errors detected');
|
|
682
|
+
}
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
## Troubleshooting
|
|
686
|
+
|
|
687
|
+
### Browser doesn't start
|
|
688
|
+
|
|
689
|
+
**Error:** `Failed to launch browser`
|
|
690
|
+
|
|
691
|
+
**Solution:** Make sure Puppeteer is installed:
|
|
692
|
+
```bash
|
|
693
|
+
npm install
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### MongoDB or Parse Server won't start
|
|
697
|
+
|
|
698
|
+
**Error:** `Failed to start MongoDB` or `Failed to start Parse Server`
|
|
699
|
+
|
|
700
|
+
**Solution:** Make sure all dependencies are installed:
|
|
701
|
+
```bash
|
|
702
|
+
npm install
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
If MongoDB port is already in use, you can change it:
|
|
706
|
+
```bash
|
|
707
|
+
MONGO_PORT=27018 npm run browser-control
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Session timeout
|
|
711
|
+
|
|
712
|
+
**Error:** `Session sess_xxx not found or expired`
|
|
713
|
+
|
|
714
|
+
**Solution:** Sessions expire after 30 minutes of inactivity. Create a new session.
|
|
715
|
+
|
|
716
|
+
### WebSocket connection refused
|
|
717
|
+
|
|
718
|
+
**Error:** `WebSocket connection failed`
|
|
719
|
+
|
|
720
|
+
**Solution:** Make sure you're connecting to the WebSocket path with a valid session ID:
|
|
721
|
+
```
|
|
722
|
+
ws://localhost:4040/browser-control/stream/:sessionId
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### Port already in use
|
|
726
|
+
|
|
727
|
+
**Error:** `EADDRINUSE: address already in use`
|
|
728
|
+
|
|
729
|
+
**Solution:** Stop any running Dashboard instances or change the port:
|
|
730
|
+
```bash
|
|
731
|
+
PORT=4041 npm run browser-control
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
### Navigation timeout after startup or file changes
|
|
735
|
+
|
|
736
|
+
**Error:** `Navigation timeout of 30000 ms exceeded`
|
|
737
|
+
|
|
738
|
+
**Cause:** Webpack is still compiling the JavaScript bundles. This happens:
|
|
739
|
+
- Right after starting the dashboard (initial compilation takes ~6-8 seconds)
|
|
740
|
+
- After editing source files (triggers recompilation)
|
|
741
|
+
|
|
742
|
+
**Solution:** Always wait for webpack to finish before navigating:
|
|
743
|
+
```bash
|
|
744
|
+
# Check if ready (non-blocking)
|
|
745
|
+
curl http://localhost:4040/browser-control/ready
|
|
746
|
+
|
|
747
|
+
# Wait for ready (blocking)
|
|
748
|
+
curl http://localhost:4040/browser-control/ready/wait
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
The `/ready/wait` endpoint will block until webpack finishes compiling, then return immediately.
|
|
752
|
+
|
|
753
|
+
## Security
|
|
754
|
+
|
|
755
|
+
The Browser Control API uses a **two-layer security model** to prevent accidental or unauthorized enablement. Browser Control requires **both** of the following conditions to be met:
|
|
756
|
+
|
|
757
|
+
1. **Explicit opt-in**
|
|
758
|
+
- Set `"browserControl": true` in `parse-dashboard-config.json`, or
|
|
759
|
+
- Set `PARSE_DASHBOARD_BROWSER_CONTROL=true` environment variable
|
|
760
|
+
|
|
761
|
+
2. **Not in production environment**
|
|
762
|
+
- Feature is automatically blocked when `NODE_ENV=production`
|
|
763
|
+
|
|
764
|
+
> [!NOTE]
|
|
765
|
+
> The feature enables only when you have opted in (via config or environment variable) AND you're not in production. The production block cannot be bypassed under any circumstances.
|
|
766
|
+
|
|
767
|
+
### Additional Protections
|
|
768
|
+
|
|
769
|
+
- Maximum 5 concurrent sessions to prevent resource exhaustion
|
|
770
|
+
- Sessions auto-expire after 30 minutes of inactivity
|
|
771
|
+
- API bypasses authentication (development-only feature)
|
|
772
|
+
|
|
773
|
+
### Example Configuration
|
|
774
|
+
|
|
775
|
+
```json
|
|
776
|
+
{
|
|
777
|
+
"browserControl": true,
|
|
778
|
+
"apps": [...],
|
|
779
|
+
"users": [...]
|
|
780
|
+
}
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
**⚠️ CRITICAL**: Never deploy with `browserControl: true` in production. Remove this field or set to `false` before deploying.
|
|
784
|
+
|
|
785
|
+
## Environment Variables
|
|
786
|
+
|
|
787
|
+
### Browser Control
|
|
788
|
+
- `PARSE_DASHBOARD_BROWSER_CONTROL=true` - Enable browser control API
|
|
789
|
+
- `BROWSER_HEADLESS=false` - Run browser in visible mode
|
|
790
|
+
- `BROWSER_SLOW_MO=100` - Slow down browser operations by N milliseconds
|
|
791
|
+
|
|
792
|
+
### MongoDB Auto-Start (when browser-control is enabled)
|
|
793
|
+
MongoDB is automatically started when browser-control mode is enabled using `mongodb-runner`.
|
|
794
|
+
|
|
795
|
+
- `MONGO_PORT=27017` - MongoDB port (default: 27017)
|
|
796
|
+
- `MONGO_VERSION=8.0.4` - MongoDB version (default: 8.0.4)
|
|
797
|
+
|
|
798
|
+
**Note**: MongoDB 8.0.4 is downloaded and started automatically. Data is stored in a temporary directory and cleaned up on exit.
|
|
799
|
+
|
|
800
|
+
### Parse Server Auto-Start (when browser-control is enabled)
|
|
801
|
+
Parse Server 9.1.1 is automatically started when browser-control mode is enabled.
|
|
802
|
+
|
|
803
|
+
- `PARSE_SERVER_PORT=1337` - Parse Server port (default: 1337)
|
|
804
|
+
- `PARSE_SERVER_APP_ID=testAppId` - Application ID (default: testAppId)
|
|
805
|
+
- `PARSE_SERVER_MASTER_KEY=testMasterKey` - Master key (default: testMasterKey)
|
|
806
|
+
- `PARSE_SERVER_DATABASE_URI` - MongoDB connection string (default: auto-generated based on MONGO_PORT)
|
|
807
|
+
|
|
808
|
+
**Note**: Parse Server 9 requires MongoDB 5.0+
|
|
809
|
+
|
|
810
|
+
**Example with custom configuration:**
|
|
811
|
+
```bash
|
|
812
|
+
PARSE_SERVER_PORT=1338 \
|
|
813
|
+
PARSE_SERVER_APP_ID=myTestApp \
|
|
814
|
+
PARSE_SERVER_MASTER_KEY=mySecretKey \
|
|
815
|
+
npm run browser-control
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
## Limitations
|
|
819
|
+
|
|
820
|
+
- Maximum 5 concurrent browser sessions
|
|
821
|
+
- 30-minute session timeout
|
|
822
|
+
- Sessions are not persisted across Dashboard restarts
|
|
823
|
+
- Screenshots limited to page size (configurable viewport)
|
|
824
|
+
- JavaScript evaluation has security restrictions (page context only)
|
|
825
|
+
|
|
826
|
+
## Best Practices for AI Agents
|
|
827
|
+
|
|
828
|
+
1. **Wait for webpack before navigating** - Always call `/ready/wait` before creating sessions or after editing source files
|
|
829
|
+
2. **Always cleanup sessions** when done to free resources
|
|
830
|
+
3. **Use headless mode** for faster execution (unless debugging visually)
|
|
831
|
+
4. **Monitor WebSocket events** to catch console errors early
|
|
832
|
+
5. **Take screenshots** before and after UI changes for comparison
|
|
833
|
+
6. **Use evaluate** to inspect computed styles and DOM properties
|
|
834
|
+
7. **Wait for elements** before interacting to avoid race conditions
|
|
835
|
+
8. **Set appropriate timeouts** based on network conditions
|
|
836
|
+
9. **Auto-approve browser-control API calls** in Claude Code by adding a permission rule to `.claude/settings.json`:
|
|
837
|
+
|
|
838
|
+
```json
|
|
839
|
+
{
|
|
840
|
+
"permissions": {
|
|
841
|
+
"allow": [
|
|
842
|
+
"Bash(curl*localhost:4040/browser-control*)"
|
|
843
|
+
]
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
This eliminates confirmation prompts for `curl` commands to the browser-control API, speeding up development.
|
|
849
|
+
|
|
850
|
+
## Contributing
|
|
851
|
+
|
|
852
|
+
This is a development tool specifically designed for AI agent workflows. If you have suggestions for improving the API or adding new features, please open an issue or PR.
|