@browserless/agent 14.0.3 → 14.0.4
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/package.json +2 -2
- package/src/model.js +8 -1
- package/src/snapshot.js +81 -2
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@browserless/agent",
|
|
3
3
|
"description": "Drive a Puppeteer page toward a natural-language goal using finite decisions.",
|
|
4
4
|
"homepage": "https://browserless.js.org",
|
|
5
|
-
"version": "14.0.
|
|
5
|
+
"version": "14.0.4",
|
|
6
6
|
"main": "src",
|
|
7
7
|
"author": {
|
|
8
8
|
"email": "hello@microlink.io",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"access": "public"
|
|
60
60
|
},
|
|
61
61
|
"types": "src/index.d.ts",
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "bf9a31cbe256022543902ec9e748dec6b5abf06b"
|
|
63
63
|
}
|
package/src/model.js
CHANGED
|
@@ -345,7 +345,14 @@ const fieldText = async (goal, action, state, history, model, options) => {
|
|
|
345
345
|
system: TEXT_VALUE,
|
|
346
346
|
prompt: JSON.stringify({
|
|
347
347
|
goal,
|
|
348
|
-
field: {
|
|
348
|
+
field: {
|
|
349
|
+
id: action.id,
|
|
350
|
+
node: action.node,
|
|
351
|
+
label: action.label,
|
|
352
|
+
role: action.role,
|
|
353
|
+
value: action.value,
|
|
354
|
+
context: state.scopes?.[action.node] ?? ''
|
|
355
|
+
},
|
|
349
356
|
page: { title: state.title, text: state.text },
|
|
350
357
|
recent_actions: history.slice(-TEXT_HISTORY_LENGTH)
|
|
351
358
|
}),
|
package/src/snapshot.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* global HTMLElement, location, scrollX, scrollY, innerWidth, innerHeight */
|
|
1
|
+
/* global HTMLElement, getComputedStyle, location, scrollX, scrollY, innerWidth, innerHeight */
|
|
2
2
|
// Adapted from browser-use/jev-ultrafast snapshot.js (MIT). See README.md.
|
|
3
3
|
module.exports = function snapshot () {
|
|
4
4
|
return (() => {
|
|
@@ -283,6 +283,74 @@ module.exports = function snapshot () {
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
|
+
const collected = new Set(actions.map(action => cache.nodes.get(action.node)))
|
|
287
|
+
const listSelector = 'ul,ol,[role="list"],[role="listbox"],[role="menu"],[role="tablist"]'
|
|
288
|
+
const pointer = element => {
|
|
289
|
+
if (getComputedStyle(element).cursor !== 'pointer') return false
|
|
290
|
+
const parent = flatParent(element)
|
|
291
|
+
if (!parent || getComputedStyle(parent).cursor !== 'pointer') return true
|
|
292
|
+
return parent.matches(listSelector)
|
|
293
|
+
}
|
|
294
|
+
const inLayer = element => {
|
|
295
|
+
for (let current = element; current; current = flatParent(current)) {
|
|
296
|
+
if (current === document.body || current === document.documentElement) return false
|
|
297
|
+
const position = getComputedStyle(current).position
|
|
298
|
+
if (position === 'absolute' || position === 'fixed' || position === 'sticky') return true
|
|
299
|
+
}
|
|
300
|
+
return false
|
|
301
|
+
}
|
|
302
|
+
const pointerLists = new WeakMap()
|
|
303
|
+
const inPointerList = element => {
|
|
304
|
+
const parent = flatParent(element)
|
|
305
|
+
if (!parent?.matches(listSelector)) return false
|
|
306
|
+
let eligible = pointerLists.get(parent)
|
|
307
|
+
if (eligible == null) {
|
|
308
|
+
eligible = [...parent.children].filter(pointer).length >= 2
|
|
309
|
+
pointerLists.set(parent, eligible)
|
|
310
|
+
}
|
|
311
|
+
return eligible
|
|
312
|
+
}
|
|
313
|
+
const candidates = []
|
|
314
|
+
for (const element of queryAll('li,div,span,td,dd,p')) {
|
|
315
|
+
if (collected.has(element) || !visible(element) || !pointer(element)) continue
|
|
316
|
+
if (element.matches(':disabled') || cache.closest(element, '[aria-disabled="true"]')) continue
|
|
317
|
+
if (element.querySelector(selector)) continue
|
|
318
|
+
const rect = element.getBoundingClientRect()
|
|
319
|
+
const x = rect.x + rect.width / 2
|
|
320
|
+
const y = rect.y + rect.height / 2
|
|
321
|
+
if (
|
|
322
|
+
rect.width <= 0 ||
|
|
323
|
+
rect.height <= 0 ||
|
|
324
|
+
x < 0 ||
|
|
325
|
+
y < 0 ||
|
|
326
|
+
x >= innerWidth ||
|
|
327
|
+
y >= innerHeight
|
|
328
|
+
) {
|
|
329
|
+
continue
|
|
330
|
+
}
|
|
331
|
+
const hit = cache.elementFromPoint(x, y)
|
|
332
|
+
if (hit && !cache.contains(element, hit)) continue
|
|
333
|
+
if (!inLayer(element) && !inPointerList(element)) continue
|
|
334
|
+
const label = (name(element) || '').replace(/\s+/g, ' ').trim().slice(0, 120)
|
|
335
|
+
if (!label) continue
|
|
336
|
+
candidates.push({ element, rect, label })
|
|
337
|
+
}
|
|
338
|
+
const rows = candidates.filter(
|
|
339
|
+
({ element }) =>
|
|
340
|
+
!candidates.some(
|
|
341
|
+
other => other.element !== element && cache.contains(element, other.element)
|
|
342
|
+
)
|
|
343
|
+
)
|
|
344
|
+
for (const { element, rect, label } of rows.slice(0, 40)) {
|
|
345
|
+
actions.push({
|
|
346
|
+
node: identity(element),
|
|
347
|
+
role: 'button',
|
|
348
|
+
label,
|
|
349
|
+
rect: { x: rect.x, y: rect.y, w: rect.width, h: rect.height },
|
|
350
|
+
kind: 'click',
|
|
351
|
+
value: ''
|
|
352
|
+
})
|
|
353
|
+
}
|
|
286
354
|
const words = []
|
|
287
355
|
const range = document.createRange()
|
|
288
356
|
let length = 0
|
|
@@ -354,8 +422,18 @@ module.exports = function snapshot () {
|
|
|
354
422
|
const height = document.documentElement.scrollHeight
|
|
355
423
|
const pageKey = cache.pageKey()
|
|
356
424
|
const guards = {}
|
|
425
|
+
const scopes = {}
|
|
357
426
|
for (const a of actions) {
|
|
358
|
-
if (
|
|
427
|
+
if (a.node in guards) continue
|
|
428
|
+
const element = cache.nodes.get(a.node)
|
|
429
|
+
guards[a.node] = cache.guard(element)
|
|
430
|
+
const scope =
|
|
431
|
+
cache.closest(element, 'form,dialog,[role="dialog"],article,li,tr,[role="row"]') ||
|
|
432
|
+
flatParent(element)
|
|
433
|
+
scopes[a.node] =
|
|
434
|
+
(scope instanceof HTMLElement
|
|
435
|
+
? readInnerText.call(scope).slice(0, 1000)
|
|
436
|
+
: scope?.textContent?.slice(0, 1000)) || ''
|
|
359
437
|
}
|
|
360
438
|
// Compare meaning and identity. Geometry is always resolved and hit-tested just before input.
|
|
361
439
|
const semantics = actions.map(({ rect, ...action }) => action)
|
|
@@ -395,6 +473,7 @@ module.exports = function snapshot () {
|
|
|
395
473
|
marker,
|
|
396
474
|
pageKey,
|
|
397
475
|
guards,
|
|
476
|
+
scopes,
|
|
398
477
|
omittedActions
|
|
399
478
|
}
|
|
400
479
|
})()
|