@nuanu-ai/agentbrowse 0.2.7 → 0.2.8
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/README.md +36 -8
- package/dist/agentpay-stagehand-llm.d.ts.map +1 -1
- package/dist/agentpay-stagehand-llm.js +5 -1
- package/dist/commands/act.d.ts +6 -2
- package/dist/commands/act.d.ts.map +1 -1
- package/dist/commands/act.js +840 -55
- package/dist/commands/act.test-harness.d.ts +19 -0
- package/dist/commands/act.test-harness.d.ts.map +1 -0
- package/dist/commands/act.test-harness.js +245 -0
- package/dist/commands/action-acceptance.d.ts +90 -0
- package/dist/commands/action-acceptance.d.ts.map +1 -0
- package/dist/commands/action-acceptance.js +1411 -0
- package/dist/commands/action-artifacts.d.ts +33 -0
- package/dist/commands/action-artifacts.d.ts.map +1 -0
- package/dist/commands/action-artifacts.js +104 -0
- package/dist/commands/action-execution-guards.d.ts +5 -0
- package/dist/commands/action-execution-guards.d.ts.map +1 -0
- package/dist/commands/action-execution-guards.js +3 -0
- package/dist/commands/action-executor-helpers.d.ts +21 -0
- package/dist/commands/action-executor-helpers.d.ts.map +1 -0
- package/dist/commands/action-executor-helpers.js +242 -0
- package/dist/commands/action-executor.d.ts +12 -0
- package/dist/commands/action-executor.d.ts.map +1 -0
- package/dist/commands/action-executor.js +45 -0
- package/dist/commands/action-fallbacks.d.ts +6 -0
- package/dist/commands/action-fallbacks.d.ts.map +1 -0
- package/dist/commands/action-fallbacks.js +43 -0
- package/dist/commands/action-value-projection.d.ts +32 -0
- package/dist/commands/action-value-projection.d.ts.map +1 -0
- package/dist/commands/action-value-projection.js +151 -0
- package/dist/commands/browse-actions.d.ts +4 -0
- package/dist/commands/browse-actions.d.ts.map +1 -0
- package/dist/commands/browse-actions.js +4 -0
- package/dist/commands/captcha-solve.d.ts.map +1 -1
- package/dist/commands/captcha-solve.js +13 -3
- package/dist/commands/click-action-executor.d.ts +10 -0
- package/dist/commands/click-action-executor.d.ts.map +1 -0
- package/dist/commands/click-action-executor.js +68 -0
- package/dist/commands/create-intent.d.ts +6 -0
- package/dist/commands/create-intent.d.ts.map +1 -0
- package/dist/commands/create-intent.js +75 -0
- package/dist/commands/datepicker-action-executor.d.ts +12 -0
- package/dist/commands/datepicker-action-executor.d.ts.map +1 -0
- package/dist/commands/datepicker-action-executor.js +218 -0
- package/dist/commands/descriptor-validation.d.ts +27 -0
- package/dist/commands/descriptor-validation.d.ts.map +1 -0
- package/dist/commands/descriptor-validation.js +333 -0
- package/dist/commands/extract-scope-resolution.d.ts +20 -0
- package/dist/commands/extract-scope-resolution.d.ts.map +1 -0
- package/dist/commands/extract-scope-resolution.js +100 -0
- package/dist/commands/extract-stagehand-executor.d.ts +17 -0
- package/dist/commands/extract-stagehand-executor.d.ts.map +1 -0
- package/dist/commands/extract-stagehand-executor.js +18 -0
- package/dist/commands/extract.d.ts +3 -2
- package/dist/commands/extract.d.ts.map +1 -1
- package/dist/commands/extract.js +256 -39
- package/dist/commands/fill-secret.d.ts +7 -0
- package/dist/commands/fill-secret.d.ts.map +1 -0
- package/dist/commands/fill-secret.js +371 -0
- package/dist/commands/get-secrets-catalog.d.ts +6 -0
- package/dist/commands/get-secrets-catalog.d.ts.map +1 -0
- package/dist/commands/get-secrets-catalog.js +23 -0
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +41 -7
- package/dist/commands/navigate.d.ts +2 -1
- package/dist/commands/navigate.d.ts.map +1 -1
- package/dist/commands/navigate.js +49 -12
- package/dist/commands/observe-inventory.d.ts +109 -0
- package/dist/commands/observe-inventory.d.ts.map +1 -0
- package/dist/commands/observe-inventory.js +2837 -0
- package/dist/commands/observe-persistence.d.ts +14 -0
- package/dist/commands/observe-persistence.d.ts.map +1 -0
- package/dist/commands/observe-persistence.js +170 -0
- package/dist/commands/observe-projection.d.ts +84 -0
- package/dist/commands/observe-projection.d.ts.map +1 -0
- package/dist/commands/observe-projection.js +140 -0
- package/dist/commands/observe-protected.d.ts +5 -0
- package/dist/commands/observe-protected.d.ts.map +1 -0
- package/dist/commands/observe-protected.js +18 -0
- package/dist/commands/observe-semantics.d.ts +10 -0
- package/dist/commands/observe-semantics.d.ts.map +1 -0
- package/dist/commands/observe-semantics.js +338 -0
- package/dist/commands/observe-stagehand.d.ts +48 -0
- package/dist/commands/observe-stagehand.d.ts.map +1 -0
- package/dist/commands/observe-stagehand.js +105 -0
- package/dist/commands/observe-surfaces.d.ts +9 -0
- package/dist/commands/observe-surfaces.d.ts.map +1 -0
- package/dist/commands/observe-surfaces.js +195 -0
- package/dist/commands/observe.d.ts +47 -1
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +173 -20
- package/dist/commands/observe.test-harness.d.ts +67 -0
- package/dist/commands/observe.test-harness.d.ts.map +1 -0
- package/dist/commands/observe.test-harness.js +107 -0
- package/dist/commands/poll-intent.d.ts +6 -0
- package/dist/commands/poll-intent.d.ts.map +1 -0
- package/dist/commands/poll-intent.js +57 -0
- package/dist/commands/screenshot.d.ts +2 -1
- package/dist/commands/screenshot.d.ts.map +1 -1
- package/dist/commands/screenshot.js +44 -12
- package/dist/commands/select-action-executor.d.ts +10 -0
- package/dist/commands/select-action-executor.d.ts.map +1 -0
- package/dist/commands/select-action-executor.js +91 -0
- package/dist/commands/semantic-observe.d.ts +24 -0
- package/dist/commands/semantic-observe.d.ts.map +1 -0
- package/dist/commands/semantic-observe.js +344 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +75 -2
- package/dist/commands/structured-grid-action-executor.d.ts +3 -0
- package/dist/commands/structured-grid-action-executor.d.ts.map +1 -0
- package/dist/commands/structured-grid-action-executor.js +4 -0
- package/dist/commands/target-resolution.d.ts +4 -0
- package/dist/commands/target-resolution.d.ts.map +1 -0
- package/dist/commands/target-resolution.js +33 -0
- package/dist/commands/text-input-action-executor.d.ts +5 -0
- package/dist/commands/text-input-action-executor.d.ts.map +1 -0
- package/dist/commands/text-input-action-executor.js +116 -0
- package/dist/commands/user-actionable.d.ts +4 -0
- package/dist/commands/user-actionable.d.ts.map +1 -0
- package/dist/commands/user-actionable.js +95 -0
- package/dist/control-semantics.d.ts +29 -0
- package/dist/control-semantics.d.ts.map +1 -0
- package/dist/control-semantics.js +299 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -32
- package/dist/output.d.ts +14 -2
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +17 -29
- package/dist/playwright-runtime.d.ts +35 -0
- package/dist/playwright-runtime.d.ts.map +1 -0
- package/dist/playwright-runtime.js +224 -0
- package/dist/runtime-resolution.d.ts +9 -0
- package/dist/runtime-resolution.d.ts.map +1 -0
- package/dist/runtime-resolution.js +19 -0
- package/dist/runtime-state.d.ts +217 -0
- package/dist/runtime-state.d.ts.map +1 -0
- package/dist/runtime-state.js +629 -0
- package/dist/secrets/backend.d.ts +32 -0
- package/dist/secrets/backend.d.ts.map +1 -0
- package/dist/secrets/backend.js +169 -0
- package/dist/secrets/catalog-applicability.d.ts +5 -0
- package/dist/secrets/catalog-applicability.d.ts.map +1 -0
- package/dist/secrets/catalog-applicability.js +59 -0
- package/dist/secrets/catalog-sync.d.ts +14 -0
- package/dist/secrets/catalog-sync.d.ts.map +1 -0
- package/dist/secrets/catalog-sync.js +35 -0
- package/dist/secrets/field-policy.d.ts +3 -0
- package/dist/secrets/field-policy.d.ts.map +1 -0
- package/dist/secrets/field-policy.js +3 -0
- package/dist/secrets/fill-ordering.d.ts +11 -0
- package/dist/secrets/fill-ordering.d.ts.map +1 -0
- package/dist/secrets/fill-ordering.js +44 -0
- package/dist/secrets/form-matcher.d.ts +60 -0
- package/dist/secrets/form-matcher.d.ts.map +1 -0
- package/dist/secrets/form-matcher.js +596 -0
- package/dist/secrets/intent-output.d.ts +11 -0
- package/dist/secrets/intent-output.d.ts.map +1 -0
- package/dist/secrets/intent-output.js +64 -0
- package/dist/secrets/mock-agentpay-backend.d.ts +13 -0
- package/dist/secrets/mock-agentpay-backend.d.ts.map +1 -0
- package/dist/secrets/mock-agentpay-backend.js +87 -0
- package/dist/secrets/mock-agentpay-cabinet.d.ts +43 -0
- package/dist/secrets/mock-agentpay-cabinet.d.ts.map +1 -0
- package/dist/secrets/mock-agentpay-cabinet.js +195 -0
- package/dist/secrets/protected-artifact-guard.d.ts +25 -0
- package/dist/secrets/protected-artifact-guard.d.ts.map +1 -0
- package/dist/secrets/protected-artifact-guard.js +26 -0
- package/dist/secrets/protected-bindings.d.ts +10 -0
- package/dist/secrets/protected-bindings.d.ts.map +1 -0
- package/dist/secrets/protected-bindings.js +17 -0
- package/dist/secrets/protected-field-values.d.ts +13 -0
- package/dist/secrets/protected-field-values.d.ts.map +1 -0
- package/dist/secrets/protected-field-values.js +100 -0
- package/dist/secrets/protected-fill.d.ts +47 -0
- package/dist/secrets/protected-fill.d.ts.map +1 -0
- package/dist/secrets/protected-fill.js +512 -0
- package/dist/secrets/types.d.ts +84 -0
- package/dist/secrets/types.d.ts.map +1 -0
- package/dist/secrets/types.js +27 -0
- package/dist/session.d.ts +22 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +74 -2
- package/dist/solver/browser-launcher.d.ts.map +1 -1
- package/dist/solver/browser-launcher.js +6 -3
- package/dist/stagehand-runtime.d.ts +4 -0
- package/dist/stagehand-runtime.d.ts.map +1 -0
- package/dist/stagehand-runtime.js +10 -0
- package/dist/stagehand.d.ts +0 -5
- package/dist/stagehand.d.ts.map +1 -1
- package/dist/stagehand.js +0 -6
- package/package.json +5 -2
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
export function normalizePageSignature(url) {
|
|
2
|
+
try {
|
|
3
|
+
const parsed = new URL(url);
|
|
4
|
+
return `${parsed.origin}${parsed.pathname}`;
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
return url;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export const LOCATOR_DOM_SIGNATURE_SCRIPT = String.raw `
|
|
11
|
+
if (!(element instanceof HTMLElement)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const inputTypeOf = () => {
|
|
16
|
+
if (element.tagName.toLowerCase() !== 'input') return '';
|
|
17
|
+
return (element.getAttribute('type') || 'text').toLowerCase();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const isButtonLikeInput = () => {
|
|
21
|
+
return element.tagName.toLowerCase() === 'input' &&
|
|
22
|
+
['button', 'submit', 'reset'].includes(inputTypeOf());
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const inferRole = () => {
|
|
26
|
+
const explicitRole = element.getAttribute('role')?.trim();
|
|
27
|
+
if (explicitRole) return explicitRole;
|
|
28
|
+
|
|
29
|
+
const tag = element.tagName.toLowerCase();
|
|
30
|
+
if (tag === 'button') return 'button';
|
|
31
|
+
if (tag === 'a' && element.getAttribute('href')) return 'link';
|
|
32
|
+
if (tag === 'select') return 'combobox';
|
|
33
|
+
if (tag === 'textarea') return 'textbox';
|
|
34
|
+
if (tag === 'input') {
|
|
35
|
+
if (isButtonLikeInput()) return 'button';
|
|
36
|
+
return 'textbox';
|
|
37
|
+
}
|
|
38
|
+
return '';
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const textOf = (target) => {
|
|
42
|
+
if (target === element && isButtonLikeInput()) {
|
|
43
|
+
const value = (element.getAttribute('value') || element.value || '').trim();
|
|
44
|
+
if (value) {
|
|
45
|
+
return value.replace(/\s+/g, ' ');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const value = target?.innerText?.trim() || target?.textContent?.trim() || '';
|
|
49
|
+
return value.replace(/\s+/g, ' ');
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const isMeaningfulLabel = (value) => {
|
|
53
|
+
const normalized = (value || '').replace(/\s+/g, ' ').trim();
|
|
54
|
+
return Boolean(normalized) && normalized !== '[object Object]';
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const composedParentElement = (node) => {
|
|
58
|
+
if (!node) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (node.parentElement) {
|
|
62
|
+
return node.parentElement;
|
|
63
|
+
}
|
|
64
|
+
const root = node.getRootNode?.();
|
|
65
|
+
return root instanceof ShadowRoot && root.host instanceof HTMLElement ? root.host : null;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const ariaLabelledbyTextOf = () => {
|
|
69
|
+
const labelledBy = element.getAttribute('aria-labelledby')?.trim();
|
|
70
|
+
if (!labelledBy) return undefined;
|
|
71
|
+
|
|
72
|
+
const text = labelledBy
|
|
73
|
+
.split(/\s+/)
|
|
74
|
+
.map((id) => textOf(document.getElementById(id)))
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
.join(' ')
|
|
77
|
+
.trim();
|
|
78
|
+
|
|
79
|
+
return isMeaningfulLabel(text) ? text : undefined;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const nearestFieldLabelOf = () => {
|
|
83
|
+
const anchors = [
|
|
84
|
+
composedParentElement(element),
|
|
85
|
+
composedParentElement(composedParentElement(element)),
|
|
86
|
+
composedParentElement(composedParentElement(composedParentElement(element))),
|
|
87
|
+
].filter(Boolean);
|
|
88
|
+
|
|
89
|
+
for (const anchor of anchors) {
|
|
90
|
+
if (!(anchor instanceof HTMLElement)) continue;
|
|
91
|
+
|
|
92
|
+
const explicitLabels = Array.from(
|
|
93
|
+
anchor.querySelectorAll('label, legend, [class*="label"], [data-testid*="label"]')
|
|
94
|
+
).filter((candidate) => {
|
|
95
|
+
return (
|
|
96
|
+
candidate instanceof HTMLElement &&
|
|
97
|
+
candidate !== element &&
|
|
98
|
+
!candidate.contains(element) &&
|
|
99
|
+
!element.contains(candidate)
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
for (const candidate of explicitLabels) {
|
|
104
|
+
const candidateText = textOf(candidate);
|
|
105
|
+
if (isMeaningfulLabel(candidateText)) {
|
|
106
|
+
return candidateText;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const child of Array.from(anchor.children).slice(0, 8)) {
|
|
111
|
+
if (!(child instanceof HTMLElement)) continue;
|
|
112
|
+
if (child === element || child.contains(element) || element.contains(child)) continue;
|
|
113
|
+
|
|
114
|
+
const candidateText = textOf(child);
|
|
115
|
+
if (isMeaningfulLabel(candidateText)) {
|
|
116
|
+
return candidateText;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return undefined;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const syntheticLabelOf = () => {
|
|
125
|
+
const tag = element.tagName.toLowerCase();
|
|
126
|
+
const explicitRole = element.getAttribute('role')?.trim();
|
|
127
|
+
if (tag === 'input') {
|
|
128
|
+
const inputType = inputTypeOf();
|
|
129
|
+
if (isButtonLikeInput()) return 'Button';
|
|
130
|
+
if (inputType === 'tel') return 'Phone input';
|
|
131
|
+
if (inputType === 'email') return 'Email input';
|
|
132
|
+
if (inputType === 'password') return 'Password input';
|
|
133
|
+
if (inputType === 'search') return 'Search input';
|
|
134
|
+
if (inputType === 'date') return 'Date input';
|
|
135
|
+
return 'Text input';
|
|
136
|
+
}
|
|
137
|
+
if (tag === 'textarea') return 'Text area';
|
|
138
|
+
if (tag === 'select' || explicitRole === 'combobox') return 'Combobox';
|
|
139
|
+
if (explicitRole === 'textbox') return 'Text input';
|
|
140
|
+
return undefined;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const labelOf = () => {
|
|
144
|
+
const ariaLabel = element.getAttribute('aria-label')?.trim();
|
|
145
|
+
if (isMeaningfulLabel(ariaLabel)) return ariaLabel;
|
|
146
|
+
|
|
147
|
+
const customLabel = element.getAttribute('label')?.trim();
|
|
148
|
+
if (isMeaningfulLabel(customLabel)) return customLabel;
|
|
149
|
+
|
|
150
|
+
const ariaLabelledbyText = ariaLabelledbyTextOf();
|
|
151
|
+
if (isMeaningfulLabel(ariaLabelledbyText)) return ariaLabelledbyText;
|
|
152
|
+
|
|
153
|
+
if ('labels' in element && Array.isArray(Array.from(element.labels ?? []))) {
|
|
154
|
+
const labels = element.labels;
|
|
155
|
+
const firstLabel = labels && labels.length > 0 ? textOf(labels[0]) : '';
|
|
156
|
+
if (isMeaningfulLabel(firstLabel)) return firstLabel;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const text = textOf(element);
|
|
160
|
+
if (isMeaningfulLabel(text)) return text;
|
|
161
|
+
|
|
162
|
+
const nearestFieldLabel = isButtonLikeInput() ? undefined : nearestFieldLabelOf();
|
|
163
|
+
if (isMeaningfulLabel(nearestFieldLabel)) return nearestFieldLabel;
|
|
164
|
+
|
|
165
|
+
return syntheticLabelOf() || '';
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
return element.tagName.toLowerCase() + '|' + inferRole() + '|' + labelOf();
|
|
169
|
+
`;
|
|
170
|
+
function readLocatorDomSignatureInBrowser(element) {
|
|
171
|
+
return Function('element', LOCATOR_DOM_SIGNATURE_SCRIPT)(element);
|
|
172
|
+
}
|
|
173
|
+
const LOCATOR_BINDING_SNAPSHOT_SCRIPT = String.raw `
|
|
174
|
+
if (!(element instanceof HTMLElement)) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const read = Function('element', ${JSON.stringify(LOCATOR_DOM_SIGNATURE_SCRIPT)});
|
|
179
|
+
const explicitRole = element.getAttribute('role')?.trim() || '';
|
|
180
|
+
const role =
|
|
181
|
+
explicitRole ||
|
|
182
|
+
(element.tagName.toLowerCase() === 'select'
|
|
183
|
+
? 'combobox'
|
|
184
|
+
: element.tagName.toLowerCase() === 'textarea'
|
|
185
|
+
? 'textbox'
|
|
186
|
+
: element.tagName.toLowerCase() === 'input'
|
|
187
|
+
? 'textbox'
|
|
188
|
+
: '');
|
|
189
|
+
const domSignature = read(element);
|
|
190
|
+
const label = typeof domSignature === 'string' ? domSignature.split('|').slice(2).join('|') : undefined;
|
|
191
|
+
return {
|
|
192
|
+
domSignature,
|
|
193
|
+
kind: element.tagName.toLowerCase(),
|
|
194
|
+
role,
|
|
195
|
+
label: label || undefined,
|
|
196
|
+
placeholder: element.getAttribute('placeholder')?.trim() || undefined,
|
|
197
|
+
inputName: element.getAttribute('name')?.trim() || undefined,
|
|
198
|
+
inputType: element.getAttribute('type')?.trim() || undefined,
|
|
199
|
+
autocomplete: element.getAttribute('autocomplete')?.trim() || undefined,
|
|
200
|
+
};
|
|
201
|
+
`;
|
|
202
|
+
export async function readLocatorDomSignature(locator) {
|
|
203
|
+
try {
|
|
204
|
+
return await locator
|
|
205
|
+
.first()
|
|
206
|
+
.evaluate((element, source) => Function('element', source)(element), LOCATOR_DOM_SIGNATURE_SCRIPT);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
export async function readLocatorBindingSnapshot(locator) {
|
|
213
|
+
try {
|
|
214
|
+
return await locator
|
|
215
|
+
.first()
|
|
216
|
+
.evaluate((element, source) => Function('element', source)(element), LOCATOR_BINDING_SNAPSHOT_SCRIPT);
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function normalizeComparable(value) {
|
|
223
|
+
const normalized = value?.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
224
|
+
return normalized && normalized.length > 0 ? normalized : undefined;
|
|
225
|
+
}
|
|
226
|
+
export function isCompatibleMutableFieldBinding(target, snapshot) {
|
|
227
|
+
if (!snapshot) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
const isFieldLike = target.controlFamily === 'text-input' ||
|
|
231
|
+
target.controlFamily === 'select' ||
|
|
232
|
+
target.controlFamily === 'datepicker' ||
|
|
233
|
+
target.allowedActions.includes('fill') ||
|
|
234
|
+
target.allowedActions.includes('type') ||
|
|
235
|
+
target.allowedActions.includes('select');
|
|
236
|
+
if (!isFieldLike) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
const comparablePairs = [
|
|
240
|
+
[normalizeComparable(target.kind), normalizeComparable(snapshot.kind)],
|
|
241
|
+
[normalizeComparable(target.semantics?.role), normalizeComparable(snapshot.role)],
|
|
242
|
+
[normalizeComparable(target.inputName), normalizeComparable(snapshot.inputName)],
|
|
243
|
+
[normalizeComparable(target.inputType), normalizeComparable(snapshot.inputType)],
|
|
244
|
+
[normalizeComparable(target.autocomplete), normalizeComparable(snapshot.autocomplete)],
|
|
245
|
+
];
|
|
246
|
+
let compared = false;
|
|
247
|
+
for (const [expected, actual] of comparablePairs) {
|
|
248
|
+
if (!expected) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
compared = true;
|
|
252
|
+
if (!actual || actual !== expected) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return compared;
|
|
257
|
+
}
|
|
258
|
+
export const __testDescriptorValidation = {
|
|
259
|
+
readLocatorDomSignatureInBrowser,
|
|
260
|
+
locatorDomSignatureScript: LOCATOR_DOM_SIGNATURE_SCRIPT,
|
|
261
|
+
locatorBindingSnapshotScript: LOCATOR_BINDING_SNAPSHOT_SCRIPT,
|
|
262
|
+
isCompatibleMutableFieldBinding,
|
|
263
|
+
};
|
|
264
|
+
export async function readLocatorOuterHtml(locator) {
|
|
265
|
+
try {
|
|
266
|
+
return await locator.first().evaluate((element) => {
|
|
267
|
+
if (!(element instanceof HTMLElement)) {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
const ownerWindow = element.ownerDocument?.defaultView || window;
|
|
271
|
+
const TextCtor = ownerWindow.Text;
|
|
272
|
+
const ElementCtor = ownerWindow.Element;
|
|
273
|
+
const ShadowRootCtor = ownerWindow.ShadowRoot;
|
|
274
|
+
const voidTags = new Set([
|
|
275
|
+
'area',
|
|
276
|
+
'base',
|
|
277
|
+
'br',
|
|
278
|
+
'col',
|
|
279
|
+
'embed',
|
|
280
|
+
'hr',
|
|
281
|
+
'img',
|
|
282
|
+
'input',
|
|
283
|
+
'link',
|
|
284
|
+
'meta',
|
|
285
|
+
'param',
|
|
286
|
+
'source',
|
|
287
|
+
'track',
|
|
288
|
+
'wbr',
|
|
289
|
+
]);
|
|
290
|
+
const escapeText = (value) => value
|
|
291
|
+
.replace(/&/g, '&')
|
|
292
|
+
.replace(/</g, '<')
|
|
293
|
+
.replace(/>/g, '>');
|
|
294
|
+
const escapeAttribute = (value) => value
|
|
295
|
+
.replace(/&/g, '&')
|
|
296
|
+
.replace(/"/g, '"')
|
|
297
|
+
.replace(/</g, '<');
|
|
298
|
+
const serializeChildren = (parent) => Array.from(parent.childNodes)
|
|
299
|
+
.map((node) => serializeNode(node))
|
|
300
|
+
.join('');
|
|
301
|
+
const serializeNode = (node) => {
|
|
302
|
+
if (node instanceof TextCtor) {
|
|
303
|
+
return escapeText(node.textContent ?? '');
|
|
304
|
+
}
|
|
305
|
+
if (!(node instanceof ElementCtor)) {
|
|
306
|
+
return '';
|
|
307
|
+
}
|
|
308
|
+
const tag = node.tagName.toLowerCase();
|
|
309
|
+
if (['script', 'style', 'noscript', 'template'].includes(tag)) {
|
|
310
|
+
return '';
|
|
311
|
+
}
|
|
312
|
+
const attrs = node
|
|
313
|
+
.getAttributeNames()
|
|
314
|
+
.sort()
|
|
315
|
+
.map((name) => ` ${name}="${escapeAttribute(node.getAttribute(name) ?? '')}"`)
|
|
316
|
+
.join('');
|
|
317
|
+
const shadowRoot = node.shadowRoot && node.shadowRoot instanceof ShadowRootCtor ? node.shadowRoot : null;
|
|
318
|
+
const shadowHtml = shadowRoot
|
|
319
|
+
? `<div data-agentbrowse-shadow-root="open">${serializeChildren(shadowRoot)}</div>`
|
|
320
|
+
: '';
|
|
321
|
+
const childHtml = serializeChildren(node);
|
|
322
|
+
if (voidTags.has(tag)) {
|
|
323
|
+
return `<${tag}${attrs}>${shadowHtml}`;
|
|
324
|
+
}
|
|
325
|
+
return `<${tag}${attrs}>${shadowHtml}${childHtml}</${tag}>`;
|
|
326
|
+
};
|
|
327
|
+
return serializeNode(element);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Page } from 'playwright-core';
|
|
2
|
+
import type { LocatorCandidate } from '../runtime-state.js';
|
|
3
|
+
export type ScopedExtractTarget = {
|
|
4
|
+
framePath?: string[];
|
|
5
|
+
locatorCandidates: LocatorCandidate[];
|
|
6
|
+
domSignature?: string;
|
|
7
|
+
};
|
|
8
|
+
export type ScopedExtractResolution = {
|
|
9
|
+
page: Page;
|
|
10
|
+
selector: string;
|
|
11
|
+
cleanup: () => Promise<void>;
|
|
12
|
+
degraded: boolean;
|
|
13
|
+
degradationReason?: 'iframe-scope-mirror';
|
|
14
|
+
};
|
|
15
|
+
export declare function resolveScopedExtractContext(args: {
|
|
16
|
+
page: Page;
|
|
17
|
+
scopeTarget: ScopedExtractTarget;
|
|
18
|
+
validateDomSignature?: boolean;
|
|
19
|
+
}): Promise<ScopedExtractResolution>;
|
|
20
|
+
//# sourceMappingURL=extract-scope-resolution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-scope-resolution.d.ts","sourceRoot":"","sources":["../../src/commands/extract-scope-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAM5D,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,qBAAqB,CAAC;CAC3C,CAAC;AAwHF,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IACtD,IAAI,EAAE,IAAI,CAAC;IACX,WAAW,EAAE,mBAAmB,CAAC;IACjC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAmBnC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { buildLocator, resolveLocatorRoot } from './action-fallbacks.js';
|
|
2
|
+
import { readLocatorDomSignature, readLocatorOuterHtml } from './descriptor-validation.js';
|
|
3
|
+
const LIVE_SCOPE_ATTRIBUTE = 'data-agentbrowse-scope';
|
|
4
|
+
async function materializeScopedExtractPage(page, scopedLocator) {
|
|
5
|
+
const scopedHtml = await readLocatorOuterHtml(scopedLocator);
|
|
6
|
+
if (!scopedHtml) {
|
|
7
|
+
throw new Error('scope_target_unresolvable');
|
|
8
|
+
}
|
|
9
|
+
const scratchPage = await page.context().newPage();
|
|
10
|
+
await scratchPage.setContent(`<!doctype html><html lang="en"><body><div id="agentbrowse-scope">${scopedHtml}</div></body></html>`);
|
|
11
|
+
return {
|
|
12
|
+
page: scratchPage,
|
|
13
|
+
selector: '#agentbrowse-scope',
|
|
14
|
+
cleanup: async () => {
|
|
15
|
+
await scratchPage.close().catch(() => undefined);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function firstVisibleLocator(locator) {
|
|
20
|
+
if (!locator) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const first = typeof locator.first === 'function' ? locator.first() : locator;
|
|
24
|
+
const count = typeof first.count === 'function' ? await first.count().catch(() => 0) : 1;
|
|
25
|
+
if (count === 0) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const visible = typeof first.isVisible === 'function' ? await first.isVisible().catch(() => false) : true;
|
|
29
|
+
return visible ? first : null;
|
|
30
|
+
}
|
|
31
|
+
async function materializeLiveScopedExtract(page, scopedLocator) {
|
|
32
|
+
const scopeId = `agentbrowse-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
33
|
+
await scopedLocator.evaluate((element, payload) => {
|
|
34
|
+
if (!(element instanceof HTMLElement)) {
|
|
35
|
+
throw new Error('unsupported_extract_scope_element');
|
|
36
|
+
}
|
|
37
|
+
element.setAttribute(payload.attribute, payload.value);
|
|
38
|
+
}, {
|
|
39
|
+
attribute: LIVE_SCOPE_ATTRIBUTE,
|
|
40
|
+
value: scopeId,
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
page,
|
|
44
|
+
selector: `[${LIVE_SCOPE_ATTRIBUTE}="${scopeId}"]`,
|
|
45
|
+
cleanup: async () => {
|
|
46
|
+
await scopedLocator
|
|
47
|
+
.evaluate((element, attribute) => {
|
|
48
|
+
if (element instanceof HTMLElement) {
|
|
49
|
+
element.removeAttribute(attribute);
|
|
50
|
+
}
|
|
51
|
+
}, LIVE_SCOPE_ATTRIBUTE)
|
|
52
|
+
.catch(() => undefined);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function resolveScopedLocator(page, scopeTarget, options) {
|
|
57
|
+
const locatorRoot = resolveLocatorRoot(page, scopeTarget.framePath);
|
|
58
|
+
let sawDomSignatureMismatch = false;
|
|
59
|
+
const validateDomSignature = options?.validateDomSignature ?? true;
|
|
60
|
+
for (const candidate of scopeTarget.locatorCandidates) {
|
|
61
|
+
const locator = buildLocator(locatorRoot, candidate);
|
|
62
|
+
const first = await firstVisibleLocator(locator);
|
|
63
|
+
if (!first) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (validateDomSignature && scopeTarget.domSignature) {
|
|
67
|
+
const liveSignature = await readLocatorDomSignature(first);
|
|
68
|
+
if (!liveSignature) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (liveSignature !== scopeTarget.domSignature) {
|
|
72
|
+
sawDomSignatureMismatch = true;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return first;
|
|
77
|
+
}
|
|
78
|
+
if (sawDomSignatureMismatch) {
|
|
79
|
+
throw new Error('stale_scope_target_dom_signature_changed');
|
|
80
|
+
}
|
|
81
|
+
throw new Error('scope_target_unresolvable');
|
|
82
|
+
}
|
|
83
|
+
export async function resolveScopedExtractContext(args) {
|
|
84
|
+
const scopedLocator = await resolveScopedLocator(args.page, args.scopeTarget, {
|
|
85
|
+
validateDomSignature: args.validateDomSignature,
|
|
86
|
+
});
|
|
87
|
+
if (args.scopeTarget.framePath?.length) {
|
|
88
|
+
const mirrored = await materializeScopedExtractPage(args.page, scopedLocator);
|
|
89
|
+
return {
|
|
90
|
+
...mirrored,
|
|
91
|
+
degraded: true,
|
|
92
|
+
degradationReason: 'iframe-scope-mirror',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const liveScoped = await materializeLiveScopedExtract(args.page, scopedLocator);
|
|
96
|
+
return {
|
|
97
|
+
...liveScoped,
|
|
98
|
+
degraded: false,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Page } from 'playwright-core';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import type { BrowseSession } from '../session.js';
|
|
4
|
+
import { type RuntimeResolution } from '../runtime-resolution.js';
|
|
5
|
+
export type ExtractStagehandExecution = RuntimeResolution & {
|
|
6
|
+
data: unknown;
|
|
7
|
+
resolvedBy: 'stagehand-extract';
|
|
8
|
+
};
|
|
9
|
+
export declare function executeStagehandExtract(args: {
|
|
10
|
+
session: BrowseSession;
|
|
11
|
+
instruction: string;
|
|
12
|
+
schema: z.ZodType;
|
|
13
|
+
page: Page;
|
|
14
|
+
selector?: string;
|
|
15
|
+
degradationReason?: string;
|
|
16
|
+
}): Promise<ExtractStagehandExecution>;
|
|
17
|
+
//# sourceMappingURL=extract-stagehand-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-stagehand-executor.d.ts","sourceRoot":"","sources":["../../src/commands/extract-stagehand-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAA8B,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG9F,MAAM,MAAM,yBAAyB,GAAG,iBAAiB,GAAG;IAC1D,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,mBAAmB,CAAC;CACjC,CAAC;AAEF,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC;IAClB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAerC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { incrementMetric } from '../runtime-state.js';
|
|
2
|
+
import { stagehandRuntimeResolution } from '../runtime-resolution.js';
|
|
3
|
+
import { withStagehand } from '../stagehand-runtime.js';
|
|
4
|
+
export async function executeStagehandExtract(args) {
|
|
5
|
+
const { session, instruction, schema, page, selector, degradationReason } = args;
|
|
6
|
+
const data = await withStagehand(session, async (stagehand) => {
|
|
7
|
+
incrementMetric(session, 'stagehandCalls');
|
|
8
|
+
return stagehand.extract(instruction, schema, {
|
|
9
|
+
page,
|
|
10
|
+
selector,
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
resolvedBy: 'stagehand-extract',
|
|
15
|
+
...stagehandRuntimeResolution(degradationReason),
|
|
16
|
+
data,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* browse extract
|
|
2
|
+
* browse extract '<schema-json>' [scopeRef] — Extract structured data from the page or a stored scope.
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
import type { BrowseSession } from '../session.js';
|
|
5
|
+
export declare function extract(session: BrowseSession, schemaJson: string, scopeRef?: string): Promise<void>;
|
|
5
6
|
//# sourceMappingURL=extract.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AA2JnD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAgKf"}
|