@dyyz1993/agent-browser 0.11.5 → 0.13.0
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/bin/agent-browser-linux-x64 +0 -0
- package/dist/__tests__/utils/parseCli.d.ts.map +1 -1
- package/dist/__tests__/utils/parseCli.js +97 -2
- package/dist/__tests__/utils/parseCli.js.map +1 -1
- package/dist/actions.d.ts +2 -1
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +254 -105
- package/dist/actions.js.map +1 -1
- package/dist/browser.d.ts +41 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +125 -30
- package/dist/browser.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +129 -5
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/connection.d.ts.map +1 -1
- package/dist/cli/connection.js +12 -29
- package/dist/cli/connection.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +35 -25
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +27 -0
- package/dist/cli/output.js.map +1 -1
- package/dist/cli.js +117 -3
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +18 -2
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +71 -33
- package/dist/daemon.js.map +1 -1
- package/dist/message-bridge.d.ts.map +1 -1
- package/dist/message-bridge.js +4 -1
- package/dist/message-bridge.js.map +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +18 -0
- package/dist/protocol.js.map +1 -1
- package/dist/rc-config.d.ts +42 -0
- package/dist/rc-config.d.ts.map +1 -0
- package/dist/rc-config.js +171 -0
- package/dist/rc-config.js.map +1 -0
- package/dist/recorder/inject.js +30 -24
- package/dist/types.d.ts +16 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
- package/scripts/generate-skill.cjs +303 -0
- package/skills/agent-browser/SKILL.md +135 -370
package/dist/browser.js
CHANGED
|
@@ -26,6 +26,9 @@ export class BrowserManager {
|
|
|
26
26
|
trackedRequests = [];
|
|
27
27
|
isRequestTrackingEnabled = false;
|
|
28
28
|
isResponseCaptureEnabled = false;
|
|
29
|
+
get trackingEnabled() {
|
|
30
|
+
return this.isRequestTrackingEnabled;
|
|
31
|
+
}
|
|
29
32
|
// Map to track requests for response matching (instance variable for cross-listener access)
|
|
30
33
|
pendingRequests = new Map();
|
|
31
34
|
// Store request listener references for proper cleanup
|
|
@@ -34,10 +37,17 @@ export class BrowserManager {
|
|
|
34
37
|
routes = new Map();
|
|
35
38
|
consoleMessages = [];
|
|
36
39
|
pageErrors = [];
|
|
40
|
+
trackedWebSockets = [];
|
|
41
|
+
isWebSocketTrackingEnabled = false;
|
|
42
|
+
wsListener = null;
|
|
43
|
+
get wsTrackingEnabled() {
|
|
44
|
+
return this.isWebSocketTrackingEnabled;
|
|
45
|
+
}
|
|
37
46
|
isRecordingHar = false;
|
|
38
47
|
refMap = {};
|
|
39
48
|
lastSnapshot = '';
|
|
40
49
|
scopedHeaderRoutes = new Map();
|
|
50
|
+
commandHistory = [];
|
|
41
51
|
// CDP session for screencast and input injection
|
|
42
52
|
cdpSession = null;
|
|
43
53
|
screencastActive = false;
|
|
@@ -91,6 +101,19 @@ export class BrowserManager {
|
|
|
91
101
|
getRefMap() {
|
|
92
102
|
return this.refMap;
|
|
93
103
|
}
|
|
104
|
+
recordCommand(action, selector, value, success) {
|
|
105
|
+
this.commandHistory.push({ action, selector, value, success, timestamp: Date.now() });
|
|
106
|
+
}
|
|
107
|
+
getHistory(filter) {
|
|
108
|
+
let history = this.commandHistory;
|
|
109
|
+
if (filter) {
|
|
110
|
+
history = history.filter((h) => h.selector.includes(filter) || h.action.includes(filter));
|
|
111
|
+
}
|
|
112
|
+
return history;
|
|
113
|
+
}
|
|
114
|
+
clearHistory() {
|
|
115
|
+
this.commandHistory = [];
|
|
116
|
+
}
|
|
94
117
|
/**
|
|
95
118
|
* Get a locator from a ref (e.g., "e1", "@e1", "ref=e1")
|
|
96
119
|
* Returns null if ref doesn't exist or is invalid
|
|
@@ -191,9 +214,12 @@ export class BrowserManager {
|
|
|
191
214
|
name: f.name(),
|
|
192
215
|
url: f.url(),
|
|
193
216
|
}));
|
|
217
|
+
const suggestion = childFrames.length > 0
|
|
218
|
+
? ` Use 'agent-browser frames' to list all iframes, or try: ${childFrames.map((f, idx) => `--in-frame "${idx}"`).join(', ')}`
|
|
219
|
+
: '';
|
|
194
220
|
throw new Error(`Frame not found for selector "${selector}" at path position ${i + 1}. ` +
|
|
195
221
|
`Path: "${framePath}". ` +
|
|
196
|
-
`Available child frames: ${JSON.stringify(availableInfo, null, 2)}`);
|
|
222
|
+
`Available child frames: ${JSON.stringify(availableInfo, null, 2)}.${suggestion}`);
|
|
197
223
|
}
|
|
198
224
|
current = matchedFrame;
|
|
199
225
|
}
|
|
@@ -228,6 +254,23 @@ export class BrowserManager {
|
|
|
228
254
|
return urlPathMatch;
|
|
229
255
|
return undefined;
|
|
230
256
|
}
|
|
257
|
+
listFrames() {
|
|
258
|
+
const page = this.getPage();
|
|
259
|
+
const result = [];
|
|
260
|
+
const walk = (frame, pathSoFar) => {
|
|
261
|
+
const children = frame.childFrames();
|
|
262
|
+
for (let i = 0; i < children.length; i++) {
|
|
263
|
+
const child = children[i];
|
|
264
|
+
const name = child.name() || '';
|
|
265
|
+
const segment = name || String(i);
|
|
266
|
+
const childPath = pathSoFar ? `${pathSoFar}/${segment}` : segment;
|
|
267
|
+
result.push({ name, url: child.url(), path: childPath });
|
|
268
|
+
walk(child, childPath);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
walk(page.mainFrame(), '');
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
231
274
|
/**
|
|
232
275
|
* Set up dialog handler
|
|
233
276
|
*/
|
|
@@ -364,6 +407,57 @@ export class BrowserManager {
|
|
|
364
407
|
clearRequests() {
|
|
365
408
|
this.trackedRequests = [];
|
|
366
409
|
}
|
|
410
|
+
startWebSocketTracking() {
|
|
411
|
+
const page = this.getPage();
|
|
412
|
+
if (this.isWebSocketTrackingEnabled)
|
|
413
|
+
return;
|
|
414
|
+
this.isWebSocketTrackingEnabled = true;
|
|
415
|
+
this.wsListener = (ws) => {
|
|
416
|
+
const id = `ws_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
417
|
+
const tracked = {
|
|
418
|
+
id,
|
|
419
|
+
url: ws.url(),
|
|
420
|
+
openedAt: Date.now(),
|
|
421
|
+
frames: [],
|
|
422
|
+
};
|
|
423
|
+
this.trackedWebSockets.push(tracked);
|
|
424
|
+
ws.on('framesent', (frame) => {
|
|
425
|
+
tracked.frames.push({
|
|
426
|
+
direction: 'send',
|
|
427
|
+
data: typeof frame.payload === 'string'
|
|
428
|
+
? frame.payload
|
|
429
|
+
: `[binary ${frame.payload.byteLength}B]`,
|
|
430
|
+
timestamp: Date.now(),
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
ws.on('framereceived', (frame) => {
|
|
434
|
+
tracked.frames.push({
|
|
435
|
+
direction: 'recv',
|
|
436
|
+
data: typeof frame.payload === 'string'
|
|
437
|
+
? frame.payload
|
|
438
|
+
: `[binary ${frame.payload.byteLength}B]`,
|
|
439
|
+
timestamp: Date.now(),
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
ws.on('socketerror', (err) => {
|
|
443
|
+
tracked.error = err;
|
|
444
|
+
});
|
|
445
|
+
ws.on('close', () => {
|
|
446
|
+
tracked.closedAt = Date.now();
|
|
447
|
+
});
|
|
448
|
+
};
|
|
449
|
+
page.on('websocket', this.wsListener);
|
|
450
|
+
}
|
|
451
|
+
getWebSockets(filter) {
|
|
452
|
+
let sockets = this.trackedWebSockets;
|
|
453
|
+
if (filter) {
|
|
454
|
+
sockets = sockets.filter((ws) => ws.url.includes(filter));
|
|
455
|
+
}
|
|
456
|
+
return sockets;
|
|
457
|
+
}
|
|
458
|
+
clearWebSockets() {
|
|
459
|
+
this.trackedWebSockets = [];
|
|
460
|
+
}
|
|
367
461
|
/**
|
|
368
462
|
* Save tracked requests to a directory
|
|
369
463
|
* @param outputDir - Directory path to save requests
|
|
@@ -748,24 +842,16 @@ export class BrowserManager {
|
|
|
748
842
|
* by verifying we can access browser contexts and that at least one has pages
|
|
749
843
|
*/
|
|
750
844
|
isCdpConnectionAlive() {
|
|
751
|
-
console.log('[DEBUG isCdpConnectionAlive] browser exists:', !!this.browser);
|
|
752
845
|
if (!this.browser)
|
|
753
846
|
return false;
|
|
754
847
|
try {
|
|
755
848
|
const contexts = this.browser.contexts();
|
|
756
|
-
console.log('[DEBUG isCdpConnectionAlive] contexts count:', contexts.length);
|
|
757
849
|
if (contexts.length === 0) {
|
|
758
|
-
console.log('[DEBUG isCdpConnectionAlive] returning false: no contexts');
|
|
759
850
|
return false;
|
|
760
851
|
}
|
|
761
|
-
|
|
762
|
-
console.log('[DEBUG isCdpConnectionAlive] pages per context:', pagesPerContext);
|
|
763
|
-
const hasPages = contexts.some((context) => context.pages().length > 0);
|
|
764
|
-
console.log('[DEBUG isCdpConnectionAlive] hasPages:', hasPages);
|
|
765
|
-
return hasPages;
|
|
852
|
+
return contexts.some((context) => context.pages().length > 0);
|
|
766
853
|
}
|
|
767
|
-
catch (
|
|
768
|
-
console.log('[DEBUG isCdpConnectionAlive] exception:', e);
|
|
854
|
+
catch (_e) {
|
|
769
855
|
return false;
|
|
770
856
|
}
|
|
771
857
|
}
|
|
@@ -773,25 +859,15 @@ export class BrowserManager {
|
|
|
773
859
|
* Check if CDP connection needs to be re-established
|
|
774
860
|
*/
|
|
775
861
|
needsCdpReconnect(cdpEndpoint) {
|
|
776
|
-
|
|
777
|
-
const endpointMatch = this.cdpEndpoint === cdpEndpoint;
|
|
778
|
-
const isAlive = this.isCdpConnectionAlive();
|
|
779
|
-
console.log('[DEBUG needsCdpReconnect] isConnected:', isConnected);
|
|
780
|
-
console.log('[DEBUG needsCdpReconnect] endpointMatch:', endpointMatch, '(this:', this.cdpEndpoint, 'vs param:', cdpEndpoint, ')');
|
|
781
|
-
console.log('[DEBUG needsCdpReconnect] isCdpConnectionAlive:', isAlive);
|
|
782
|
-
if (!isConnected) {
|
|
783
|
-
console.log('[DEBUG needsCdpReconnect] returning true: not connected');
|
|
862
|
+
if (!this.browser?.isConnected()) {
|
|
784
863
|
return true;
|
|
785
864
|
}
|
|
786
|
-
if (
|
|
787
|
-
console.log('[DEBUG needsCdpReconnect] returning true: endpoint mismatch');
|
|
865
|
+
if (this.cdpEndpoint !== cdpEndpoint) {
|
|
788
866
|
return true;
|
|
789
867
|
}
|
|
790
|
-
if (!
|
|
791
|
-
console.log('[DEBUG needsCdpReconnect] returning true: not alive');
|
|
868
|
+
if (!this.isCdpConnectionAlive()) {
|
|
792
869
|
return true;
|
|
793
870
|
}
|
|
794
|
-
console.log('[DEBUG needsCdpReconnect] returning false: all checks passed');
|
|
795
871
|
return false;
|
|
796
872
|
}
|
|
797
873
|
/**
|
|
@@ -1259,8 +1335,6 @@ export class BrowserManager {
|
|
|
1259
1335
|
context.setDefaultTimeout(60000);
|
|
1260
1336
|
this.contexts.push(context);
|
|
1261
1337
|
this.setupContextTracking(context);
|
|
1262
|
-
// Set up context tracking to catch window.open() popups
|
|
1263
|
-
this.setupContextTracking(context);
|
|
1264
1338
|
const page = context.pages()[0] ?? (await context.newPage());
|
|
1265
1339
|
// Only add if not already tracked (setupContextTracking may have already added it via 'page' event)
|
|
1266
1340
|
if (!this.pages.includes(page)) {
|
|
@@ -1434,8 +1508,6 @@ export class BrowserManager {
|
|
|
1434
1508
|
this.setupPageTracking(page);
|
|
1435
1509
|
}
|
|
1436
1510
|
this.activePageIndex = this.pages.length - 1;
|
|
1437
|
-
// Set up tracking for the new page
|
|
1438
|
-
this.setupPageTracking(page);
|
|
1439
1511
|
// Trigger tab created event callback
|
|
1440
1512
|
const callbacks = getEventCallbacks();
|
|
1441
1513
|
if (callbacks.onTabCreated) {
|
|
@@ -1468,8 +1540,6 @@ export class BrowserManager {
|
|
|
1468
1540
|
this.setupPageTracking(page);
|
|
1469
1541
|
}
|
|
1470
1542
|
this.activePageIndex = this.pages.length - 1;
|
|
1471
|
-
// Set up tracking for the new page
|
|
1472
|
-
this.setupPageTracking(page);
|
|
1473
1543
|
// Trigger tab created event callback
|
|
1474
1544
|
const callbacks = getEventCallbacks();
|
|
1475
1545
|
if (callbacks.onTabCreated) {
|
|
@@ -2705,6 +2775,22 @@ export class BrowserManager {
|
|
|
2705
2775
|
return `agent-browser click "xpath=${escapeShell(step.xpath)}"`;
|
|
2706
2776
|
}
|
|
2707
2777
|
return null;
|
|
2778
|
+
case 'check':
|
|
2779
|
+
if (step.selector) {
|
|
2780
|
+
return `agent-browser check "${escapeShell(step.selector)}"`;
|
|
2781
|
+
}
|
|
2782
|
+
if (step.xpath) {
|
|
2783
|
+
return `agent-browser check "xpath=${escapeShell(step.xpath)}"`;
|
|
2784
|
+
}
|
|
2785
|
+
return null;
|
|
2786
|
+
case 'uncheck':
|
|
2787
|
+
if (step.selector) {
|
|
2788
|
+
return `agent-browser uncheck "${escapeShell(step.selector)}"`;
|
|
2789
|
+
}
|
|
2790
|
+
if (step.xpath) {
|
|
2791
|
+
return `agent-browser uncheck "xpath=${escapeShell(step.xpath)}"`;
|
|
2792
|
+
}
|
|
2793
|
+
return null;
|
|
2708
2794
|
case 'fill':
|
|
2709
2795
|
if (step.value !== undefined) {
|
|
2710
2796
|
if (step.selector) {
|
|
@@ -2845,6 +2931,15 @@ export class BrowserManager {
|
|
|
2845
2931
|
this.pendingRequests.clear();
|
|
2846
2932
|
this.isRequestTrackingEnabled = false;
|
|
2847
2933
|
this.isResponseCaptureEnabled = false;
|
|
2934
|
+
if (this.wsListener) {
|
|
2935
|
+
try {
|
|
2936
|
+
page?.off('websocket', this.wsListener);
|
|
2937
|
+
}
|
|
2938
|
+
catch { }
|
|
2939
|
+
this.wsListener = null;
|
|
2940
|
+
}
|
|
2941
|
+
this.isWebSocketTrackingEnabled = false;
|
|
2942
|
+
this.trackedWebSockets = [];
|
|
2848
2943
|
this.routes.clear();
|
|
2849
2944
|
this.consoleMessages = [];
|
|
2850
2945
|
this.pageErrors = [];
|