agent-browser 0.17.1 → 0.19.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/dist/actions.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
+ import { exec } from 'node:child_process';
3
4
  import { mkdirSync } from 'node:fs';
4
5
  import { getAppDir } from './daemon.js';
5
6
  import { checkPolicy, describeAction, getActionCategory, loadPolicyFile, initPolicyReloader, reloadPolicyIfChanged, } from './action-policy.js';
@@ -229,6 +230,10 @@ async function dispatchAction(command, browser) {
229
230
  return await handleReload(command, browser);
230
231
  case 'url':
231
232
  return await handleUrl(command, browser);
233
+ case 'cdp_url':
234
+ return handleCdpUrl(command, browser);
235
+ case 'inspect':
236
+ return await handleInspect(command, browser);
232
237
  case 'title':
233
238
  return await handleTitle(command, browser);
234
239
  case 'getattribute':
@@ -406,19 +411,11 @@ async function handleLaunch(command, browser) {
406
411
  return successResponse(command.id, { launched: true });
407
412
  }
408
413
  async function handleNavigate(command, browser) {
409
- browser.checkDomainAllowed(command.url);
410
- const page = browser.getPage();
411
- // If headers are provided, set up scoped headers for this origin
412
- if (command.headers && Object.keys(command.headers).length > 0) {
413
- await browser.setScopedHeaders(command.url, command.headers);
414
- }
415
- await page.goto(command.url, {
416
- waitUntil: command.waitUntil ?? 'load',
417
- });
418
- return successResponse(command.id, {
419
- url: page.url(),
420
- title: await page.title(),
414
+ const result = await browser.navigate(command.url, {
415
+ headers: command.headers,
416
+ waitUntil: command.waitUntil,
421
417
  });
418
+ return successResponse(command.id, result);
422
419
  }
423
420
  async function handleClick(command, browser) {
424
421
  // Support both refs (@e1) and regular selectors
@@ -508,7 +505,7 @@ async function handleScreenshot(command, browser) {
508
505
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
509
506
  const random = Math.random().toString(36).substring(2, 8);
510
507
  const filename = `screenshot-${timestamp}-${random}.${ext}`;
511
- const screenshotDir = path.join(getAppDir(), 'tmp', 'screenshots');
508
+ const screenshotDir = command.screenshotDir ?? path.join(getAppDir(), 'tmp', 'screenshots');
512
509
  mkdirSync(screenshotDir, { recursive: true });
513
510
  savePath = path.join(screenshotDir, filename);
514
511
  }
@@ -691,7 +688,10 @@ async function handleEvaluate(command, browser) {
691
688
  }
692
689
  async function handleWait(command, browser) {
693
690
  const page = browser.getPage();
694
- if (command.selector) {
691
+ if (command.text) {
692
+ await page.waitForFunction(`(document.body.innerText || '').includes(${JSON.stringify(command.text)})`, { timeout: command.timeout });
693
+ }
694
+ else if (command.selector) {
695
695
  await page.waitForSelector(command.selector, {
696
696
  state: command.state ?? 'visible',
697
697
  timeout: command.timeout,
@@ -701,7 +701,6 @@ async function handleWait(command, browser) {
701
701
  await page.waitForTimeout(command.timeout);
702
702
  }
703
703
  else {
704
- // Default: wait for load state
705
704
  await page.waitForLoadState('load');
706
705
  }
707
706
  return successResponse(command.id, { waited: true });
@@ -767,7 +766,8 @@ async function handleContent(command, browser) {
767
766
  const page = browser.getPage();
768
767
  let html;
769
768
  if (command.selector) {
770
- html = await page.locator(command.selector).innerHTML();
769
+ const locator = browser.getLocator(command.selector);
770
+ html = await locator.innerHTML();
771
771
  }
772
772
  else {
773
773
  html = await page.content();
@@ -1145,6 +1145,62 @@ async function handleUrl(command, browser) {
1145
1145
  const page = browser.getPage();
1146
1146
  return successResponse(command.id, { url: page.url() });
1147
1147
  }
1148
+ function handleCdpUrl(command, browser) {
1149
+ const cdpUrl = browser.getCdpUrl();
1150
+ if (!cdpUrl) {
1151
+ return errorResponse(command.id, 'CDP URL not available (browser may not be launched)');
1152
+ }
1153
+ return successResponse(command.id, { cdpUrl });
1154
+ }
1155
+ async function handleInspect(command, browser) {
1156
+ const cdpUrl = browser.getCdpUrl();
1157
+ if (!cdpUrl) {
1158
+ return errorResponse(command.id, 'CDP URL not available (browser may not be launched)');
1159
+ }
1160
+ // Shut down any existing inspect server so we always target the current page
1161
+ browser.stopInspectServer();
1162
+ const stripped = cdpUrl.replace(/^(wss?|https?):\/\//, '');
1163
+ const hostPort = stripped.split('/')[0];
1164
+ // Get the target ID so the inspect server can create its own dedicated CDP session
1165
+ const page = browser.getPage();
1166
+ const context = page.context();
1167
+ const tmpCdp = await context.newCDPSession(page);
1168
+ let targetId = '';
1169
+ try {
1170
+ const info = await tmpCdp.send('Target.getTargetInfo');
1171
+ targetId = info?.targetInfo?.targetId || '';
1172
+ }
1173
+ catch (err) {
1174
+ console.error('[inspect] getTargetInfo failed:', err);
1175
+ }
1176
+ await tmpCdp.detach();
1177
+ if (!targetId) {
1178
+ return errorResponse(command.id, 'Could not determine target ID for active page');
1179
+ }
1180
+ const { InspectServer } = await import('./inspect-server.js');
1181
+ const server = new InspectServer({
1182
+ chromeHostPort: hostPort,
1183
+ targetId,
1184
+ chromeWsUrl: cdpUrl,
1185
+ });
1186
+ await server.start();
1187
+ browser.setInspectServer(server);
1188
+ const url = `http://127.0.0.1:${server.port}`;
1189
+ openUrlInBrowser(url);
1190
+ return successResponse(command.id, { opened: true, url });
1191
+ }
1192
+ function openUrlInBrowser(url) {
1193
+ const platform = process.platform;
1194
+ const cmd = platform === 'darwin'
1195
+ ? `open "${url}"`
1196
+ : platform === 'win32'
1197
+ ? `start "" "${url}"`
1198
+ : `xdg-open "${url}"`;
1199
+ exec(cmd, (err) => {
1200
+ if (err)
1201
+ console.error('[inspect] Failed to open browser:', err.message);
1202
+ });
1203
+ }
1148
1204
  async function handleTitle(command, browser) {
1149
1205
  const page = browser.getPage();
1150
1206
  const title = await page.title();
@@ -1159,7 +1215,8 @@ async function handleGetAttribute(command, browser) {
1159
1215
  async function handleGetText(command, browser) {
1160
1216
  const page = browser.getPage();
1161
1217
  const locator = browser.getLocator(command.selector);
1162
- const text = await locator.textContent();
1218
+ const inner = await locator.innerText();
1219
+ const text = inner || (await locator.textContent()) || '';
1163
1220
  return successResponse(command.id, { text, origin: page.url() });
1164
1221
  }
1165
1222
  async function handleIsVisible(command, browser) {
@@ -1178,13 +1235,13 @@ async function handleIsChecked(command, browser) {
1178
1235
  return successResponse(command.id, { checked });
1179
1236
  }
1180
1237
  async function handleCount(command, browser) {
1181
- const page = browser.getPage();
1182
- const count = await page.locator(command.selector).count();
1238
+ const locator = browser.getLocator(command.selector);
1239
+ const count = await locator.count();
1183
1240
  return successResponse(command.id, { count });
1184
1241
  }
1185
1242
  async function handleBoundingBox(command, browser) {
1186
- const page = browser.getPage();
1187
- const box = await page.locator(command.selector).boundingBox();
1243
+ const locator = browser.getLocator(command.selector);
1244
+ const box = await locator.boundingBox();
1188
1245
  return successResponse(command.id, { box });
1189
1246
  }
1190
1247
  async function handleStyles(command, browser) {
@@ -1303,8 +1360,6 @@ async function handleStateLoad(command, browser) {
1303
1360
  return errorResponse(command.id, `State file not found: ${command.path}`);
1304
1361
  }
1305
1362
  await browser.launch({
1306
- id: command.id,
1307
- action: 'launch',
1308
1363
  headless: true,
1309
1364
  autoStateFilePath: command.path,
1310
1365
  });
@@ -1472,7 +1527,7 @@ async function handleKeyboard(command, browser) {
1472
1527
  async function handleWheel(command, browser) {
1473
1528
  const page = browser.getPage();
1474
1529
  if (command.selector) {
1475
- const element = page.locator(command.selector);
1530
+ const element = browser.getLocator(command.selector);
1476
1531
  await element.hover();
1477
1532
  }
1478
1533
  await page.mouse.wheel(command.deltaX ?? 0, command.deltaY ?? 0);
@@ -1487,41 +1542,50 @@ async function handleClipboard(command, browser) {
1487
1542
  const page = browser.getPage();
1488
1543
  switch (command.operation) {
1489
1544
  case 'copy':
1490
- await page.keyboard.press('Control+c');
1545
+ await page.keyboard.press('ControlOrMeta+c');
1491
1546
  return successResponse(command.id, { copied: true });
1492
1547
  case 'paste':
1493
- await page.keyboard.press('Control+v');
1548
+ await page.keyboard.press('ControlOrMeta+v');
1494
1549
  return successResponse(command.id, { pasted: true });
1495
- case 'read':
1550
+ case 'read': {
1496
1551
  const text = await page.evaluate('navigator.clipboard.readText()');
1497
1552
  return successResponse(command.id, { text });
1553
+ }
1554
+ case 'write': {
1555
+ if (!command.text) {
1556
+ return errorResponse(command.id, "Missing 'text' parameter for clipboard write");
1557
+ }
1558
+ await page.evaluate(`navigator.clipboard.writeText(${JSON.stringify(command.text)})`);
1559
+ return successResponse(command.id, { written: command.text });
1560
+ }
1498
1561
  default:
1499
1562
  return errorResponse(command.id, 'Unknown clipboard operation');
1500
1563
  }
1501
1564
  }
1502
1565
  async function handleHighlight(command, browser) {
1503
- const page = browser.getPage();
1504
- await page.locator(command.selector).highlight();
1566
+ const locator = browser.getLocator(command.selector);
1567
+ await locator.highlight();
1505
1568
  return successResponse(command.id, { highlighted: true });
1506
1569
  }
1507
1570
  async function handleClear(command, browser) {
1508
- const page = browser.getPage();
1509
- await page.locator(command.selector).clear();
1571
+ const locator = browser.getLocator(command.selector);
1572
+ await locator.clear();
1510
1573
  return successResponse(command.id, { cleared: true });
1511
1574
  }
1512
1575
  async function handleSelectAll(command, browser) {
1513
- const page = browser.getPage();
1514
- await page.locator(command.selector).selectText();
1576
+ const locator = browser.getLocator(command.selector);
1577
+ await locator.selectText();
1515
1578
  return successResponse(command.id, { selected: true });
1516
1579
  }
1517
1580
  async function handleInnerText(command, browser) {
1518
- const page = browser.getPage();
1519
- const text = await page.locator(command.selector).innerText();
1581
+ const locator = browser.getLocator(command.selector);
1582
+ const text = await locator.innerText();
1520
1583
  return successResponse(command.id, { text });
1521
1584
  }
1522
1585
  async function handleInnerHtml(command, browser) {
1523
1586
  const page = browser.getPage();
1524
- const html = await page.locator(command.selector).innerHTML();
1587
+ const locator = browser.getLocator(command.selector);
1588
+ const html = await locator.innerHTML();
1525
1589
  return successResponse(command.id, { html, origin: page.url() });
1526
1590
  }
1527
1591
  async function handleInputValue(command, browser) {
@@ -1531,13 +1595,13 @@ async function handleInputValue(command, browser) {
1531
1595
  return successResponse(command.id, { value, origin: page.url() });
1532
1596
  }
1533
1597
  async function handleSetValue(command, browser) {
1534
- const page = browser.getPage();
1535
- await page.locator(command.selector).fill(command.value);
1598
+ const locator = browser.getLocator(command.selector);
1599
+ await locator.fill(command.value);
1536
1600
  return successResponse(command.id, { set: true });
1537
1601
  }
1538
1602
  async function handleDispatch(command, browser) {
1539
- const page = browser.getPage();
1540
- await page.locator(command.selector).dispatchEvent(command.event, command.eventInit);
1603
+ const locator = browser.getLocator(command.selector);
1604
+ await locator.dispatchEvent(command.event, command.eventInit);
1541
1605
  return successResponse(command.id, { dispatched: command.event });
1542
1606
  }
1543
1607
  async function handleEvalHandle(command, browser) {
@@ -1643,8 +1707,7 @@ async function handleGetByTestId(command, browser) {
1643
1707
  }
1644
1708
  }
1645
1709
  async function handleNth(command, browser) {
1646
- const page = browser.getPage();
1647
- const base = page.locator(command.selector);
1710
+ const base = browser.getLocator(command.selector);
1648
1711
  const locator = command.index === -1 ? base.last() : base.nth(command.index);
1649
1712
  switch (command.subaction) {
1650
1713
  case 'click':
@@ -1754,8 +1817,8 @@ async function handleInsertText(command, browser) {
1754
1817
  return successResponse(command.id, { inserted: true });
1755
1818
  }
1756
1819
  async function handleMultiSelect(command, browser) {
1757
- const page = browser.getPage();
1758
- const selected = await page.locator(command.selector).selectOption(command.values);
1820
+ const locator = browser.getLocator(command.selector);
1821
+ const selected = await locator.selectOption(command.values);
1759
1822
  return successResponse(command.id, { selected });
1760
1823
  }
1761
1824
  async function handleWaitForDownload(command, browser) {
@@ -1903,7 +1966,7 @@ async function handleDiffScreenshot(command, browser) {
1903
1966
  const page = browser.getPage();
1904
1967
  let screenshotBuffer;
1905
1968
  if (command.selector) {
1906
- const locator = browser.getLocatorFromRef(command.selector) || page.locator(command.selector);
1969
+ const locator = browser.getLocator(command.selector);
1907
1970
  screenshotBuffer = await locator.screenshot({ type: 'png' });
1908
1971
  }
1909
1972
  else {